drivers/i2c/ptn3460: Provide chip driver for PTN3460
This patch provides a chip driver for the DP-2-LVDS bridge PTN3460.
The bridge is configured via I2C. As the mainboard has all the
information regarding the attached LCD type, there are three hooks into
mainboard code to get the information like EDID data and PTN config.
TEST=Display is working on Siemens mainboards (e.g. mc_tcu3, mc_apl1, ...).
Change-Id: Ie4c8176cd16836fa5b8fd2f72faf7a55723b82f6
Signed-off-by: Uwe Poeche <uwe.poeche@siemens.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/36642
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Werner Zeh <werner.zeh@siemens.com>
diff --git a/src/drivers/i2c/ptn3460/Kconfig b/src/drivers/i2c/ptn3460/Kconfig
new file mode 100644
index 0000000..6dcdbc0
--- /dev/null
+++ b/src/drivers/i2c/ptn3460/Kconfig
@@ -0,0 +1,5 @@
+config DRIVERS_I2C_PTN3460
+ bool
+ default n
+ help
+ Enable support for external display bridge (eDP to LVDS) PTN3460.
diff --git a/src/drivers/i2c/ptn3460/Makefile.inc b/src/drivers/i2c/ptn3460/Makefile.inc
new file mode 100644
index 0000000..abe9a05
--- /dev/null
+++ b/src/drivers/i2c/ptn3460/Makefile.inc
@@ -0,0 +1 @@
+ramstage-$(CONFIG_DRIVERS_I2C_PTN3460) += ptn3460.c
diff --git a/src/drivers/i2c/ptn3460/chip.h b/src/drivers/i2c/ptn3460/chip.h
new file mode 100644
index 0000000..8bd6d9e
--- /dev/null
+++ b/src/drivers/i2c/ptn3460/chip.h
@@ -0,0 +1,3 @@
+struct drivers_i2c_ptn3460_config {
+
+};
diff --git a/src/drivers/i2c/ptn3460/ptn3460.c b/src/drivers/i2c/ptn3460/ptn3460.c
new file mode 100644
index 0000000..ef25745
--- /dev/null
+++ b/src/drivers/i2c/ptn3460/ptn3460.c
@@ -0,0 +1,157 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2019 Siemens AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+#include <console/console.h>
+#include <device/i2c_bus.h>
+#include <types.h>
+
+#include "ptn3460.h"
+
+/**
+ * \brief This function selects one of 7 EDID-tables inside PTN3460
+ * which should be emulated on display port and turn emulation ON
+ * @param *dev Pointer to the relevant I2C controller
+ * @param edid_num Number of EDID to emulate (0..6)
+ * @return PTN_SUCCESS or error code
+ */
+static int ptn_select_edid(struct device *dev, uint8_t edid_num)
+{
+ int status = 0;
+ u8 val;
+
+ if (edid_num > PTN_MAX_EDID_NUM)
+ return PTN_INVALID_EDID;
+ val = (edid_num << 1) | PTN_ENABLE_EMULATION;
+ status = i2c_dev_writeb_at(dev, PTN_CONFIG_OFF + 4, val);
+ return status ? (PTN_BUS_ERROR | status) : PTN_SUCCESS;
+}
+
+/**
+ * \brief This function writes one EDID data structure to PTN3460
+ * @param *dev Pointer to the relevant I2C controller
+ * @param edid_num Number of EDID that must be written (0..6)
+ * @param *data Pointer to a buffer where data to write is stored in
+ * @return PTN_SUCCESS on success or error code
+ */
+static int ptn3460_write_edid(struct device *dev, u8 edid_num, u8 *data)
+{
+ int status;
+ int i;
+
+ if (edid_num > PTN_MAX_EDID_NUM)
+ return PTN_INVALID_EDID;
+
+ /* First enable access to the desired EDID table */
+ status = i2c_dev_writeb_at(dev, PTN_CONFIG_OFF + 5, edid_num);
+ if (status)
+ return (PTN_BUS_ERROR | status);
+
+ /* Now we can simply write EDID data to ptn3460 */
+ for (i = 0; i < PTN_EDID_LEN; i++) {
+ status = i2c_dev_writeb_at(dev, PTN_EDID_OFF + i, data[i]);
+ if (status)
+ return (PTN_BUS_ERROR | status);
+ }
+ return PTN_SUCCESS;
+}
+
+/**
+ * \brief This function sets up the DP2LVDS-converter to be used with the
+ * appropriate EDID data
+ * @param *dev Pointer to the I2C controller where PTN3460 is attached
+ */
+static void ptn3460_init(struct device *dev)
+{
+ struct ptn_3460_config cfg;
+ uint8_t edid_data[PTN_EDID_LEN], edid_tab, *ptr = (uint8_t *) &cfg;
+ int i, val;
+
+ /* Mainboard provides EDID data. */
+ if (mb_get_edid(edid_data) != CB_SUCCESS) {
+ printk(BIOS_ERR, "PTN3460 error: Unable to get EDID data from mainboard.\n");
+ return;
+ }
+
+ /* Mainboard decides which EDID table has to be used. */
+ edid_tab = mb_select_edid_table();
+ if (edid_tab > PTN_MAX_EDID_NUM) {
+ printk(BIOS_ERR, "PTN3460 error: invalid EDID table (%d) selected.\n",
+ edid_tab);
+ return;
+ }
+ /* Write EDID data into PTN. */
+ val = ptn3460_write_edid(dev, edid_tab, edid_data);
+ if (val != PTN_SUCCESS) {
+ printk(BIOS_ERR, "PTN3460 error: writing EDID data into device failed.\n");
+ return;
+ }
+ /* Activate the selected EDID block. */
+ ptn_select_edid(dev, edid_tab);
+ /* Read out PTN configuration data. */
+ for (i = 0; i < sizeof(struct ptn_3460_config); i++) {
+ val = i2c_dev_readb_at(dev, PTN_CONFIG_OFF + i);
+ if (val < 0) {
+ printk(BIOS_ERR,
+ "PTN3460 error: Unable to read config data from device.\n");
+ return;
+ }
+ *ptr++ = (uint8_t)val; /* fill config structure via ptr */
+ }
+ /* Mainboard can modify the configuration data.
+ Write back configuration data to PTN3460 if modified by mainboard */
+ if (mb_adjust_cfg(&cfg) == PTN_CFG_MODIFIED) {
+ ptr = (uint8_t *) &cfg;
+ for (i = 0; i < sizeof(struct ptn_3460_config); i++) {
+ val = i2c_dev_writeb_at(dev, PTN_CONFIG_OFF + i, *ptr++);
+ if (val < 0) {
+ printk(BIOS_ERR,
+ "PTN3460 error: Unable to write config data.\n");
+ return;
+ }
+ }
+ }
+}
+
+__weak enum cb_err mb_get_edid(uint8_t edid_data[0x80])
+{
+ return CB_ERR;
+}
+__weak uint8_t mb_select_edid_table(void)
+{
+ return 0;
+}
+__weak int mb_adjust_cfg(struct ptn_3460_config *cfg_ptr)
+{
+ return 0;
+}
+
+static struct device_operations ptn3460_ops = {
+ .read_resources = DEVICE_NOOP,
+ .set_resources = DEVICE_NOOP,
+ .enable_resources = DEVICE_NOOP,
+ .init = ptn3460_init,
+ .final = DEVICE_NOOP
+};
+
+static void ptn3460_enable(struct device *dev)
+{
+ dev->ops = &ptn3460_ops;
+}
+
+struct chip_operations drivers_i2c_ptn3460_ops = {
+ CHIP_NAME("PTN3460")
+ .enable_dev = ptn3460_enable
+};
diff --git a/src/drivers/i2c/ptn3460/ptn3460.h b/src/drivers/i2c/ptn3460/ptn3460.h
new file mode 100644
index 0000000..4b9834e
--- /dev/null
+++ b/src/drivers/i2c/ptn3460/ptn3460.h
@@ -0,0 +1,73 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2019 Siemens AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _I2C_PTN3460_H_
+#define _I2C_PTN3460_H_
+
+#include <stdint.h>
+
+#define PTN_EDID_OFF 0x00
+#define PTN_EDID_LEN 0x80
+#define PTN_CONFIG_OFF 0x80
+#define PTN_CONFIG_LEN 0x19
+#define PTN_FLASH_CFG_OFF 0xE8
+#define PTN_FLASH_CFG_LEN 0x04
+#define PTN_MAX_EDID_NUM 6
+#define PTN_ENABLE_EMULATION (1 << 0)
+
+/* Define some error codes that can be used */
+#define PTN_SUCCESS 0x00000000
+#define PTN_CFG_MODIFIED 0x00000001
+#define PTN_BUS_ERROR 0x10000000
+#define PTN_INVALID_EDID 0x20000000
+#define PTN_INVALID_EDID_BLOCK 0x30000000
+#define PTN_ERROR 0x40000000
+
+struct ptn_3460_config {
+ u8 dp_interface_ctrl; /* DisplayPort interface control */
+ u8 lvds_interface_ctrl1; /* LVDS interface control register 1 */
+ u8 lvds_interface_ctrl2; /* LVDS interface control register 2 */
+ u8 lvds_interface_ctrl3; /* LVDS interface control register 3 */
+ u8 edid_rom_emulation; /* select which EDID-block is emulated */
+ u8 edid_rom_access_ctrl; /* select which EDID block to map to 0..0x7F */
+ u8 pwm_min[3]; /* smallest PWM frequency for back light */
+ u8 pwm_max[3]; /* biggest PWM frequency for back light */
+ u8 fast_link_ctrl; /* Fast link training control register */
+ u8 pin_cfg_ctrl1; /* Pin configuration control register 1 */
+ u8 pin_cfg_ctrl2; /* Pin configuration control register 2 */
+ u8 pwm_default; /* Default PWM bit count in DPCD register */
+ u16 pwm_value; /* Current PWM bit count in DPCD register */
+ u8 pwm_default_freq; /* Default PWM frequency in DPCD register */
+ u8 t3_timing; /* Panel T3 timing value */
+ u8 t12_timing; /* Panel T12 timing value */
+ u8 backlight_ctrl; /* Back light control register */
+ u8 t2_delay; /* Panel T2 delay */
+ u8 t4_timing; /* Panel T4 timing value */
+ u8 t5_delay; /* Panel T5 delay */
+} __packed;
+
+struct ptn_3460_flash {
+ u8 cmd; /* Flash command (erase or erase and flash) */
+ u16 magic; /* Magic number needed by the flash algorithm */
+ u8 trigger; /* Trigger for starting flash operation */
+} __packed;
+
+/* We need functions which we can call to get mainboard specific data */
+/* These functions can be implemented somewhere else but must exist. */
+extern enum cb_err mb_get_edid(uint8_t edid_data[0x80]);
+extern uint8_t mb_select_edid_table(void);
+extern int mb_adjust_cfg(struct ptn_3460_config *cfg_ptr);
+
+#endif /* _I2C_PTN3460_H_ */