xref: /aosp_15_r20/external/coreboot/src/drivers/i2c/ptn3460/ptn3460.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
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