0575a4f8ee
Do not abort the initialization of PTN3460 if HW-ID could not be retrieved and just assume that the HW-ID does not match 7.9.2.0. In this case PTN3460 will be setup to a working condition even if this field is missing. This makes this driver more robust with faulty blocks. Change-Id: I301fb165a7924768e44182d92be820294beb0280 Signed-off-by: Werner Zeh <werner.zeh@siemens.com> Reviewed-on: https://review.coreboot.org/17671 Tested-by: build bot (Jenkins) Reviewed-by: Mario Scheithauer <mario.scheithauer@siemens.com>
203 lines
6.6 KiB
C
203 lines
6.6 KiB
C
/*
|
|
* This file is part of the coreboot project.
|
|
*
|
|
* Copyright (C) 2014 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 "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};
|
|
|
|
if (!hwi_block || hwilib_find_blocks(hwi_block) != CB_SUCCESS) {
|
|
printk(BIOS_ERR, "LCD: Info block \"%s\" not found!\n",
|
|
hwi_block);
|
|
return 1;
|
|
}
|
|
|
|
status = i2c_init(PTN_I2C_CONTROLER);
|
|
if (status)
|
|
return (PTN_BUS_ERROR | status);
|
|
|
|
/* 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(PTN_I2C_CONTROLER, PTN_SLAVE_ADR, PTN_CONFIG_OFF,
|
|
(u8*)&cfg, PTN_CONFIG_LEN);
|
|
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 */
|
|
status = i2c_write(PTN_I2C_CONTROLER, PTN_SLAVE_ADR, PTN_CONFIG_OFF,
|
|
(u8*)&cfg, PTN_CONFIG_LEN);
|
|
if (status)
|
|
return (PTN_BUS_ERROR | status);
|
|
else
|
|
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_write(PTN_I2C_CONTROLER, PTN_SLAVE_ADR, PTN_CONFIG_OFF + 5,
|
|
&edid_num, 1);
|
|
if (status)
|
|
return (PTN_BUS_ERROR | status);
|
|
|
|
/* Now we can simply read back EDID-data */
|
|
status = i2c_read(PTN_I2C_CONTROLER, 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;
|
|
|
|
if (edid_num > PTN_MAX_EDID_NUM)
|
|
return PTN_INVALID_EDID;
|
|
/* First enable access to the desired EDID table */
|
|
status = i2c_write(PTN_I2C_CONTROLER, PTN_SLAVE_ADR, PTN_CONFIG_OFF + 5,
|
|
&edid_num, 1);
|
|
if (status)
|
|
return (PTN_BUS_ERROR | status);
|
|
|
|
/* Now we can simply write EDID-data to ptn3460 */
|
|
status = i2c_write(PTN_I2C_CONTROLER, 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 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_write(PTN_I2C_CONTROLER, PTN_SLAVE_ADR, PTN_CONFIG_OFF + 4,
|
|
&val, 1);
|
|
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;
|
|
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 */
|
|
status = i2c_write(PTN_I2C_CONTROLER, PTN_SLAVE_ADR, PTN_FLASH_CFG_OFF,
|
|
(u8*)&flash, PTN_FLASH_CFG_LEN);
|
|
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;
|
|
}
|
|
}
|