blob: 93acb1834e9875260c2c233a2145e9f3bf791fdd [file] [log] [blame]
Angel Pons8a3453f2020-04-02 23:48:19 +02001/* SPDX-License-Identifier: GPL-2.0-only */
Uwe Poeche11a34ec2019-10-28 11:28:50 +01002
Uwe Poeche11a34ec2019-10-28 11:28:50 +01003#include <console/console.h>
Werner Zeha1a3be12022-12-22 09:25:07 +01004#include <device/device.h>
Uwe Poeche11a34ec2019-10-28 11:28:50 +01005#include <device/i2c_bus.h>
6#include <types.h>
Jan Samek66918442022-09-15 16:31:44 +02007#include <bootstate.h>
Uwe Poeche11a34ec2019-10-28 11:28:50 +01008
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 */
18static 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 */
37static 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 */
64static void ptn3460_init(struct device *dev)
65{
66 struct ptn_3460_config cfg;
Elyes Haouas1ef547e2022-11-18 15:05:39 +010067 uint8_t edid_data[PTN_EDID_LEN], edid_tab, *ptr = (uint8_t *)&cfg;
Uwe Poeche11a34ec2019-10-28 11:28:50 +010068 int i, val;
69
Jan Samek66918442022-09-15 16:31:44 +020070 /* 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
Uwe Poeche11a34ec2019-10-28 11:28:50 +010078 /* Mainboard provides EDID data. */
Jan Sameka3b29d72023-01-31 14:09:30 +010079 if (mainboard_ptn3460_get_edid(edid_data) != CB_SUCCESS) {
Uwe Poeche11a34ec2019-10-28 11:28:50 +010080 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. */
Jan Sameka3b29d72023-01-31 14:09:30 +010085 edid_tab = mainboard_ptn3460_select_edid_table();
Uwe Poeche11a34ec2019-10-28 11:28:50 +010086 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 */
Jan Sameka3b29d72023-01-31 14:09:30 +0100111 if (mainboard_ptn3460_config(&cfg) == CB_SUCCESS) {
Elyes Haouas1ef547e2022-11-18 15:05:39 +0100112 ptr = (uint8_t *)&cfg;
Uwe Poeche11a34ec2019-10-28 11:28:50 +0100113 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 }
Jan Samek66918442022-09-15 16:31:44 +0200122
123 init_done = true;
Uwe Poeche11a34ec2019-10-28 11:28:50 +0100124}
125
Jan Sameka3b29d72023-01-31 14:09:30 +0100126__weak enum cb_err mainboard_ptn3460_get_edid(uint8_t edid_data[PTN_EDID_LEN])
Uwe Poeche11a34ec2019-10-28 11:28:50 +0100127{
128 return CB_ERR;
129}
Jan Sameka3b29d72023-01-31 14:09:30 +0100130__weak uint8_t mainboard_ptn3460_select_edid_table(void)
Uwe Poeche11a34ec2019-10-28 11:28:50 +0100131{
132 return 0;
133}
Jan Sameka3b29d72023-01-31 14:09:30 +0100134__weak enum cb_err mainboard_ptn3460_config(struct ptn_3460_config *cfg_ptr)
Uwe Poeche11a34ec2019-10-28 11:28:50 +0100135{
Jan Samek79312af2023-01-18 13:28:50 +0100136 return CB_ERR;
Uwe Poeche11a34ec2019-10-28 11:28:50 +0100137}
138
139static struct device_operations ptn3460_ops = {
Nico Huber2f8ba692020-04-05 14:05:24 +0200140 .read_resources = noop_read_resources,
141 .set_resources = noop_set_resources,
Uwe Poeche11a34ec2019-10-28 11:28:50 +0100142 .init = ptn3460_init,
Uwe Poeche11a34ec2019-10-28 11:28:50 +0100143};
144
145static void ptn3460_enable(struct device *dev)
146{
147 dev->ops = &ptn3460_ops;
148}
149
150struct chip_operations drivers_i2c_ptn3460_ops = {
Nicholas Sudsgaardbfb11be2024-01-30 09:53:46 +0900151 .name = "PTN3460",
Uwe Poeche11a34ec2019-10-28 11:28:50 +0100152 .enable_dev = ptn3460_enable
153};
Jan Samek66918442022-09-15 16:31:44 +0200154
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
164static 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 }
Werner Zeha1a3be12022-12-22 09:25:07 +0100174 /* Initialize the I2C controller before it is used. */
175 if (ptn_dev->bus && ptn_dev->bus->dev->ops && ptn_dev->bus->dev->ops->init)
176 ptn_dev->bus->dev->ops->init(ptn_dev->bus->dev);
Jan Samek66918442022-09-15 16:31:44 +0200177 ptn3460_init(ptn_dev);
178}
179
180BOOT_STATE_INIT_ENTRY(BS_DEV_INIT, BS_ON_ENTRY, ptn3460_early_init, NULL);
181
182#endif /* CONFIG(PTN3460_EARLY_INIT) */