ed309e58b0
Refactor I2C driver for fsp_baytrail to match the coreboot supported I2C bus device structure. The internal I2C controllers are now handled by the generic PCI driver approach and generic I2C access is enabled. As orientation for the I2C code the actual solution from soc/intel/apollolake I2C was taken. All the I2C specific parts were removed from lpss.c and have been implemented in the I2C driver. Future merge to soc/intel/common/block/i2c/i2c.c would be possible. With this patch I2C chip devices can now be used in devicetree. TEST=Booted siemens/tcu3 and verified that access to PTN3460 worked. Change-Id: I3b87bd7c27e4c1afcce7cd4225cca02599f43c60 Signed-off-by: Uwe Poeche <uwe.poeche@siemens.com> Reviewed-on: https://review.coreboot.org/c/coreboot/+/36062 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Werner Zeh <werner.zeh@siemens.com>
209 lines
6.8 KiB
C
209 lines
6.8 KiB
C
/*
|
|
* This file is part of the coreboot project.
|
|
*
|
|
* Copyright (C) 2014-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 <hwilib.h>
|
|
#include <string.h>
|
|
#include <delay.h>
|
|
#include <types.h>
|
|
#include <device/i2c_simple.h>
|
|
#include "soc/i2c.h"
|
|
#include "ptn3460.h"
|
|
|
|
/** \brief This functions sets up the DP2LVDS-converter to be used with the
|
|
* appropriate lcd panel
|
|
* @param *hwi_block Filename in CBFS of the block to use as HW-Info
|
|
* @return 0 on success or error code
|
|
*/
|
|
int ptn3460_init(char *hwi_block)
|
|
{
|
|
struct ptn_3460_config cfg;
|
|
int status;
|
|
uint8_t disp_con = 0, color_depth = 0;
|
|
uint8_t edid_data[0x80];
|
|
uint8_t hwid[4], tcu31_hwid[4] = {7, 9, 2, 0};
|
|
uint8_t i;
|
|
|
|
if (!hwi_block || hwilib_find_blocks(hwi_block) != CB_SUCCESS) {
|
|
printk(BIOS_ERR, "LCD: Info block \"%s\" not found!\n",
|
|
hwi_block);
|
|
return 1;
|
|
}
|
|
|
|
/* Get all needed information from hwinfo block */
|
|
if (hwilib_get_field(Edid, edid_data, 0x80) != sizeof(edid_data)) {
|
|
printk(BIOS_ERR, "LCD: No EDID data available in %s\n",
|
|
hwi_block);
|
|
return 1;
|
|
}
|
|
if ((hwilib_get_field(PF_DisplCon, &disp_con, 1) != 1)) {
|
|
printk(BIOS_ERR, "LCD: Missing panel features from %s\n",
|
|
hwi_block);
|
|
return 1;
|
|
}
|
|
if (hwilib_get_field(PF_Color_Depth, &color_depth, 1) != 1) {
|
|
printk(BIOS_ERR, "LCD: Missing panel features from %s\n",
|
|
hwi_block);
|
|
return 1;
|
|
}
|
|
/* Here, all the desired information for setting up DP2LVDS converter*/
|
|
/* are present. Inside the converter, table 6 will be used for */
|
|
/* the timings. */
|
|
if ((status = ptn3460_write_edid(6, edid_data)) != 0)
|
|
return status;
|
|
/* Select this table to be emulated */
|
|
ptn_select_edid(6);
|
|
/* Read PTN configuration data */
|
|
status = i2c_read_bytes(PTN_I2C_CONTROLLER, PTN_SLAVE_ADR, PTN_CONFIG_OFF, (u8 *) &cfg,
|
|
sizeof(struct ptn_3460_config));
|
|
if (status)
|
|
return (PTN_BUS_ERROR | status);
|
|
|
|
/* Set up configuration data according to the hwinfo blocks we get */
|
|
cfg.dp_interface_ctrl = 0;
|
|
cfg.lvds_interface_ctrl1 = 0x00;
|
|
if (disp_con == PF_DISPLCON_LVDS_DUAL)
|
|
cfg.lvds_interface_ctrl1 |= 0x0b; /* Turn on dual LVDS lane and clock */
|
|
if (color_depth == PF_COLOR_DEPTH_6BIT)
|
|
cfg.lvds_interface_ctrl1 |= 0x20; /* Use 18 bits per pixel */
|
|
cfg.lvds_interface_ctrl2 = 0x03; /* no clock spreading, 300 mV LVDS swing */
|
|
/* Swap LVDS even and odd lanes for HW-ID 7.9.2.0 only. */
|
|
if (hwilib_get_field(HWID, hwid, sizeof(hwid)) == sizeof(hwid) &&
|
|
!(memcmp(hwid, tcu31_hwid, sizeof(hwid)))) {
|
|
cfg.lvds_interface_ctrl3 = 0x01; /* swap LVDS even and odd */
|
|
} else
|
|
cfg.lvds_interface_ctrl3 = 0x00; /* no LVDS signal swap */
|
|
cfg.t2_delay = 1; /* Delay T2 (VDD to LVDS active) by 16 ms */
|
|
cfg.t3_timing = 10; /* 500 ms from LVDS to backlight active */
|
|
cfg.t12_timing = 20; /* 1 second re-power delay */
|
|
cfg.t4_timing = 3; /* 150 ms backlight off to LVDS inactive */
|
|
cfg.t5_delay = 1; /* Delay T5 (LVDS to VDD inactive) by 16 ms */
|
|
cfg.backlight_ctrl = 0; /* Enable backlight control */
|
|
|
|
/* Write back configuration data to PTN3460 */
|
|
for (i = 0; i < sizeof(struct ptn_3460_config); i++) {
|
|
status = i2c_writeb(PTN_I2C_CONTROLLER, PTN_SLAVE_ADR, PTN_CONFIG_OFF + i,
|
|
*(((uint8_t *) &cfg) + i));
|
|
if (status)
|
|
return (PTN_BUS_ERROR | status);
|
|
}
|
|
|
|
return PTN_NO_ERROR;
|
|
}
|
|
|
|
/** \brief This functions reads one desired EDID data structure from PTN3460
|
|
* @param edid_num Number of EDID that must be read (0..6)
|
|
* @param *data Pointer to a buffer where to store read data
|
|
* @return 0 on success or error code
|
|
*/
|
|
int ptn3460_read_edid(u8 edid_num, u8 *data)
|
|
{
|
|
int status;
|
|
|
|
if (edid_num > PTN_MAX_EDID_NUM)
|
|
return PTN_INVALID_EDID;
|
|
/* First enable access to the desired EDID table */
|
|
status = i2c_writeb(PTN_I2C_CONTROLLER, PTN_SLAVE_ADR, PTN_CONFIG_OFF + 5, edid_num);
|
|
if (status)
|
|
return (PTN_BUS_ERROR | status);
|
|
|
|
/* Now we can simply read back EDID-data */
|
|
status = i2c_read_bytes(PTN_I2C_CONTROLLER, PTN_SLAVE_ADR, PTN_EDID_OFF, data,
|
|
PTN_EDID_LEN);
|
|
if (status)
|
|
return (PTN_BUS_ERROR | status);
|
|
else
|
|
return PTN_NO_ERROR;
|
|
}
|
|
|
|
/** \brief This functions writes one EDID data structure to PTN3460
|
|
* @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 0 on success or error code
|
|
*/
|
|
int ptn3460_write_edid(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_writeb(PTN_I2C_CONTROLLER, PTN_SLAVE_ADR, 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_writeb(PTN_I2C_CONTROLLER, PTN_SLAVE_ADR, PTN_EDID_OFF + i,
|
|
data[i]);
|
|
if (status)
|
|
return (PTN_BUS_ERROR | status);
|
|
}
|
|
return PTN_NO_ERROR;
|
|
}
|
|
|
|
/** \brief This functions selects one of 7 EDID-tables inside PTN3460
|
|
* which should be emulated on display port and turn emulation ON
|
|
* @param edid_num Number of EDID to emulate (0..6)
|
|
* @return 0 on success or error code
|
|
*/
|
|
int ptn_select_edid (u8 edid_num)
|
|
{
|
|
int status;
|
|
u8 val;
|
|
|
|
if (edid_num > PTN_MAX_EDID_NUM)
|
|
return PTN_INVALID_EDID;
|
|
/* Enable emulation of the desired EDID table */
|
|
val = (edid_num << 1) | 1;
|
|
status = i2c_writeb(PTN_I2C_CONTROLLER, PTN_SLAVE_ADR, PTN_CONFIG_OFF + 4, val);
|
|
if (status)
|
|
return (PTN_BUS_ERROR | status);
|
|
else
|
|
return PTN_NO_ERROR;
|
|
}
|
|
|
|
/** \brief This functions performs a flash operation which will write
|
|
* current configuration table (all the EDID-tables and the
|
|
* configuration space with a total amount of 1 KByte)
|
|
* to the internal flash of PTN3460
|
|
* @param none
|
|
* @return 0 on success or error code
|
|
*/
|
|
int ptn3460_flash_config(void)
|
|
{
|
|
int status, i;
|
|
struct ptn_3460_flash flash;
|
|
|
|
flash.cmd = 0x01; /* perform erase and flash cycle */
|
|
flash.magic = 0x7845; /* Magic number to protect flash operation */
|
|
flash.trigger = 0x56; /* This value starts flash operation */
|
|
|
|
for (i = 0; i < sizeof(struct ptn_3460_flash); i++) {
|
|
status = i2c_writeb(PTN_I2C_CONTROLLER, PTN_SLAVE_ADR, PTN_FLASH_CFG_OFF+i,
|
|
*(((uint8_t *) &flash) + i));
|
|
if (status)
|
|
return (PTN_BUS_ERROR | status);
|
|
}
|
|
if (status) {
|
|
return (PTN_BUS_ERROR | status);
|
|
} else {
|
|
/* To ensure the flash operation is finished, we have to wait 300 ms */
|
|
mdelay(300);
|
|
return PTN_NO_ERROR;
|
|
}
|
|
}
|