mb/google/brya: Create rull variant
[coreboot2.git] / src / drivers / i2c / ptn3460 / ptn3460.c
blob76b3bc813be92f77ca641909173e0b6a0bf0f25e
1 /* SPDX-License-Identifier: GPL-2.0-only */
3 #include <console/console.h>
4 #include <device/device.h>
5 #include <device/i2c_bus.h>
6 #include <types.h>
7 #include <bootstate.h>
9 #include "ptn3460.h"
11 /**
12 * \brief This function selects one of 7 EDID-tables inside PTN3460
13 * which should be emulated on display port and turn emulation ON
14 * @param *dev Pointer to the relevant I2C controller
15 * @param edid_num Number of EDID to emulate (0..6)
16 * @return PTN_SUCCESS or error code
18 static int ptn_select_edid(struct device *dev, uint8_t edid_num)
20 int status = 0;
21 u8 val;
23 if (edid_num > PTN_MAX_EDID_NUM)
24 return PTN_INVALID_EDID;
25 val = (edid_num << 1) | PTN_ENABLE_EMULATION;
26 status = i2c_dev_writeb_at(dev, PTN_CONFIG_OFF + 4, val);
27 return status ? (PTN_BUS_ERROR | status) : PTN_SUCCESS;
30 /**
31 * \brief This function writes one EDID data structure to PTN3460
32 * @param *dev Pointer to the relevant I2C controller
33 * @param edid_num Number of EDID that must be written (0..6)
34 * @param *data Pointer to a buffer where data to write is stored in
35 * @return PTN_SUCCESS on success or error code
37 static int ptn3460_write_edid(struct device *dev, u8 edid_num, u8 *data)
39 int status;
40 int i;
42 if (edid_num > PTN_MAX_EDID_NUM)
43 return PTN_INVALID_EDID;
45 /* First enable access to the desired EDID table */
46 status = i2c_dev_writeb_at(dev, PTN_CONFIG_OFF + 5, edid_num);
47 if (status)
48 return (PTN_BUS_ERROR | status);
50 /* Now we can simply write EDID data to ptn3460 */
51 for (i = 0; i < PTN_EDID_LEN; i++) {
52 status = i2c_dev_writeb_at(dev, PTN_EDID_OFF + i, data[i]);
53 if (status)
54 return (PTN_BUS_ERROR | status);
56 return PTN_SUCCESS;
59 /**
60 * \brief This function sets up the DP2LVDS-converter to be used with the
61 * appropriate EDID data
62 * @param *dev Pointer to the I2C controller where PTN3460 is attached
64 static void ptn3460_init(struct device *dev)
66 struct ptn_3460_config cfg;
67 uint8_t edid_data[PTN_EDID_LEN], edid_tab, *ptr = (uint8_t *)&cfg;
68 int i, val;
70 /* Guard against re-initialization of the device */
71 static bool init_done = false;
73 if (init_done) {
74 printk(BIOS_DEBUG, "Skipping PTN3460 init as it's already initialized\n");
75 return;
78 /* Mainboard provides EDID data. */
79 if (mainboard_ptn3460_get_edid(edid_data) != CB_SUCCESS) {
80 printk(BIOS_ERR, "PTN3460 error: Unable to get EDID data from mainboard.\n");
81 return;
84 /* Mainboard decides which EDID table has to be used. */
85 edid_tab = mainboard_ptn3460_select_edid_table();
86 if (edid_tab > PTN_MAX_EDID_NUM) {
87 printk(BIOS_ERR, "PTN3460 error: invalid EDID table (%d) selected.\n",
88 edid_tab);
89 return;
91 /* Write EDID data into PTN. */
92 val = ptn3460_write_edid(dev, edid_tab, edid_data);
93 if (val != PTN_SUCCESS) {
94 printk(BIOS_ERR, "PTN3460 error: writing EDID data into device failed.\n");
95 return;
97 /* Activate the selected EDID block. */
98 ptn_select_edid(dev, edid_tab);
99 /* Read out PTN configuration data. */
100 for (i = 0; i < sizeof(struct ptn_3460_config); i++) {
101 val = i2c_dev_readb_at(dev, PTN_CONFIG_OFF + i);
102 if (val < 0) {
103 printk(BIOS_ERR,
104 "PTN3460 error: Unable to read config data from device.\n");
105 return;
107 *ptr++ = (uint8_t)val; /* fill config structure via ptr */
109 /* Mainboard can modify the configuration data.
110 Write back configuration data to PTN3460 if modified by mainboard */
111 if (mainboard_ptn3460_config(&cfg) == CB_SUCCESS) {
112 ptr = (uint8_t *)&cfg;
113 for (i = 0; i < sizeof(struct ptn_3460_config); i++) {
114 val = i2c_dev_writeb_at(dev, PTN_CONFIG_OFF + i, *ptr++);
115 if (val < 0) {
116 printk(BIOS_ERR,
117 "PTN3460 error: Unable to write config data.\n");
118 return;
123 init_done = true;
126 __weak enum cb_err mainboard_ptn3460_get_edid(uint8_t edid_data[PTN_EDID_LEN])
128 return CB_ERR;
130 __weak uint8_t mainboard_ptn3460_select_edid_table(void)
132 return 0;
134 __weak enum cb_err mainboard_ptn3460_config(struct ptn_3460_config *cfg_ptr)
136 return CB_ERR;
139 static struct device_operations ptn3460_ops = {
140 .read_resources = noop_read_resources,
141 .set_resources = noop_set_resources,
142 .init = ptn3460_init,
145 static void ptn3460_enable(struct device *dev)
147 dev->ops = &ptn3460_ops;
150 struct chip_operations drivers_i2c_ptn3460_ops = {
151 .name = "PTN3460",
152 .enable_dev = ptn3460_enable
155 #if CONFIG(PTN3460_EARLY_INIT)
158 * \brief This function provides a callback for the boot state machine to initialize the
159 * PTN3460 DP-to-LVDS bridge before graphics initialization in order for the bootsplash
160 * logo to be shown.
161 * @param *unused Unused argument for the callback.
164 static void ptn3460_early_init(void *unused)
166 struct device *ptn_dev;
168 printk(BIOS_DEBUG, "Attempting PTN3460 early init.\n");
169 ptn_dev = dev_find_slot_on_smbus(0, CONFIG_PTN3460_EARLY_ADDR);
170 if (!ptn_dev) {
171 printk(BIOS_ERR, "Failed to find the PTN3460 device!\n");
172 return;
174 /* Initialize the I2C controller before it is used. */
175 if (ptn_dev->upstream && ptn_dev->upstream->dev->ops && ptn_dev->upstream->dev->ops->init)
176 ptn_dev->upstream->dev->ops->init(ptn_dev->upstream->dev);
177 ptn3460_init(ptn_dev);
180 BOOT_STATE_INIT_ENTRY(BS_DEV_INIT, BS_ON_ENTRY, ptn3460_early_init, NULL);
182 #endif /* CONFIG(PTN3460_EARLY_INIT) */