1 /* SPDX-License-Identifier: GPL-2.0-only */
2
3 #include <console/console.h>
4 #include <device/device.h>
5 #include <device/i2c_bus.h>
6 #include <types.h>
7 #include <bootstate.h>
8
9 #include "ptn3460.h"
10
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
17 */
ptn_select_edid(struct device * dev,uint8_t edid_num)18 static int ptn_select_edid(struct device *dev, uint8_t edid_num)
19 {
20 int status = 0;
21 u8 val;
22
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;
28 }
29
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
36 */
ptn3460_write_edid(struct device * dev,u8 edid_num,u8 * data)37 static int ptn3460_write_edid(struct device *dev, u8 edid_num, u8 *data)
38 {
39 int status;
40 int i;
41
42 if (edid_num > PTN_MAX_EDID_NUM)
43 return PTN_INVALID_EDID;
44
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);
49
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);
55 }
56 return PTN_SUCCESS;
57 }
58
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
63 */
ptn3460_init(struct device * dev)64 static void ptn3460_init(struct device *dev)
65 {
66 struct ptn_3460_config cfg;
67 uint8_t edid_data[PTN_EDID_LEN], edid_tab, *ptr = (uint8_t *)&cfg;
68 int i, val;
69
70 /* Guard against re-initialization of the device */
71 static bool init_done = false;
72
73 if (init_done) {
74 printk(BIOS_DEBUG, "Skipping PTN3460 init as it's already initialized\n");
75 return;
76 }
77
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;
82 }
83
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;
90 }
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;
96 }
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;
106 }
107 *ptr++ = (uint8_t)val; /* fill config structure via ptr */
108 }
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;
119 }
120 }
121 }
122
123 init_done = true;
124 }
125
mainboard_ptn3460_get_edid(uint8_t edid_data[PTN_EDID_LEN])126 __weak enum cb_err mainboard_ptn3460_get_edid(uint8_t edid_data[PTN_EDID_LEN])
127 {
128 return CB_ERR;
129 }
mainboard_ptn3460_select_edid_table(void)130 __weak uint8_t mainboard_ptn3460_select_edid_table(void)
131 {
132 return 0;
133 }
mainboard_ptn3460_config(struct ptn_3460_config * cfg_ptr)134 __weak enum cb_err mainboard_ptn3460_config(struct ptn_3460_config *cfg_ptr)
135 {
136 return CB_ERR;
137 }
138
139 static struct device_operations ptn3460_ops = {
140 .read_resources = noop_read_resources,
141 .set_resources = noop_set_resources,
142 .init = ptn3460_init,
143 };
144
ptn3460_enable(struct device * dev)145 static void ptn3460_enable(struct device *dev)
146 {
147 dev->ops = &ptn3460_ops;
148 }
149
150 struct chip_operations drivers_i2c_ptn3460_ops = {
151 .name = "PTN3460",
152 .enable_dev = ptn3460_enable
153 };
154
155 #if CONFIG(PTN3460_EARLY_INIT)
156
157 /**
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.
162 */
163
ptn3460_early_init(void * unused)164 static void ptn3460_early_init(void *unused)
165 {
166 struct device *ptn_dev;
167
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;
173 }
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);
178 }
179
180 BOOT_STATE_INIT_ENTRY(BS_DEV_INIT, BS_ON_ENTRY, ptn3460_early_init, NULL);
181
182 #endif /* CONFIG(PTN3460_EARLY_INIT) */
183