mb/google/corsola: Add support for MIPI panel

The detachable Starmie will use MIPI panels, which require reading
serializable data from the CBFS. So we add MIPI panel support to the
display configuration and align the configuration sequence with the
panels that use MIPI bridges.

The PMIC Datasheet:
TPS65132-Single-Inductor-Dual-Output-Power-Supply.pdf

BUG=b:275470328
BRANCH=corsola
TEST=emerge-corsola coreboot chromeos-bootimage and display normally

Signed-off-by: Ruihai Zhou <zhouruihai@huaqin.corp-partner.google.com>
Change-Id: I6f079e54f0317ff2f685f0e3834ebd1ceb8e9fcb
Reviewed-on: https://review.coreboot.org/c/coreboot/+/74051
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Yidi Lin <yidilin@google.com>
Reviewed-by: Yu-Ping Wu <yupingso@google.com>
This commit is contained in:
Ruihai Zhou 2023-03-28 16:49:01 +08:00 committed by Lean Sheng Tan
parent 946d17a2a5
commit d5c1e13304
6 changed files with 325 additions and 131 deletions

View File

@ -19,5 +19,9 @@ ramstage-y += boardid.c
ramstage-y += chromeos.c
ramstage-y += display.c
ramstage-y += mainboard.c
ramstage-y += panel_anx7625.c
ramstage-y += panel_ps8640.c
ramstage-y += regulator.c
ramstage-y += reset.c
ramstage-$(CONFIG_BOARD_GOOGLE_STARMIE) += panel_starmie.c

View File

@ -2,109 +2,21 @@
#include <assert.h>
#include <boardid.h>
#include <cbfs.h>
#include <console/console.h>
#include <delay.h>
#include <drivers/analogix/anx7625/anx7625.h>
#include <drivers/parade/ps8640/ps8640.h>
#include <device/i2c_simple.h>
#include <edid.h>
#include <gpio.h>
#include <soc/ddp.h>
#include <soc/dsi.h>
#include <soc/gpio_common.h>
#include <soc/regulator.h>
#include <soc/i2c.h>
#include <soc/mtcmos.h>
#include "display.h"
#include "gpio.h"
/* Bridge functions */
static void bridge_ps8640_power_on(void)
{
/*
* PS8640 power-on sequence is described in chapter 14, PS8640_DS_V1.4_20200210.docx
* - set VDD12 to be 1.2V
* - delay 100us
* - set VDD33 to be 3.3V
* - pull hign PD#
* - pull down RST#
* - delay 2ms
* - pull high RST#
* - delay more than 50ms (55ms for margin)
* - pull down RST#
* - delay more than 50ms (55ms for margin)
* - pull high RST#
*/
/* Set VRF12 to 1.2V and VCN33 to 3.3V */
mainboard_set_regulator_voltage(MTK_REGULATOR_VRF12, 1200000);
udelay(100);
mainboard_set_regulator_voltage(MTK_REGULATOR_VCN33, 3300000);
udelay(200);
/* Turn on bridge */
gpio_output(GPIO_EDPBRDG_PWREN, 1);
gpio_output(GPIO_EDPBRDG_RST_L, 0);
mdelay(2);
gpio_output(GPIO_EDPBRDG_RST_L, 1);
mdelay(55);
gpio_output(GPIO_EDPBRDG_RST_L, 0);
mdelay(55);
gpio_output(GPIO_EDPBRDG_RST_L, 1);
}
static int bridge_ps8640_get_edid(u8 i2c_bus, struct edid *edid)
{
const u8 chip = 0x8;
if (ps8640_init(i2c_bus, chip) < 0) {
printk(BIOS_ERR, "%s: Can't init PS8640 bridge\n", __func__);
return -1;
}
if (ps8640_get_edid(i2c_bus, chip, edid) < 0) {
printk(BIOS_ERR, "%s: Can't get panel's edid\n", __func__);
return -1;
}
return 0;
}
static int bridge_ps8640_post_power_on(u8 i2c_bus, struct edid *edid)
{
/* Do nothing */
return 0;
}
static void bridge_anx7625_power_on(void)
{
/* Turn on bridge */
gpio_output(GPIO_EDPBRDG_RST_L, 0);
gpio_output(GPIO_EN_PP1000_EDPBRDG, 1);
gpio_output(GPIO_EN_PP1800_EDPBRDG, 1);
gpio_output(GPIO_EN_PP3300_EDPBRDG, 1);
mdelay(14);
gpio_output(GPIO_EDPBRDG_PWREN, 1);
mdelay(80);
gpio_output(GPIO_EDPBRDG_RST_L, 1);
}
static int bridge_anx7625_get_edid(u8 i2c_bus, struct edid *edid)
{
if (anx7625_init(i2c_bus) < 0) {
printk(BIOS_ERR, "%s: Can't init ANX7625 bridge\n", __func__);
return -1;
}
if (anx7625_dp_get_edid(i2c_bus, edid) < 0) {
printk(BIOS_ERR, "%s: Can't get panel's edid\n", __func__);
return -1;
}
return 0;
}
static int bridge_anx7625_post_power_on(u8 i2c_bus, struct edid *edid)
{
return anx7625_dp_start(i2c_bus, edid);
}
/* Display function */
static void backlight_control(void)
{
/* Disable backlight before turning on bridge */
@ -113,52 +25,118 @@ static void backlight_control(void)
gpio_output(GPIO_EN_PP3300_DISP_X, 1);
}
static const struct edp_bridge anx7625_bridge = {
.power_on = bridge_anx7625_power_on,
.get_edid = bridge_anx7625_get_edid,
.post_power_on = bridge_anx7625_post_power_on,
};
int panel_pmic_reg_mask(unsigned int bus, uint8_t chip, uint8_t addr,
uint8_t val, uint8_t mask)
{
uint8_t msg = 0;
static const struct edp_bridge ps8640_bridge = {
.power_on = bridge_ps8640_power_on,
.get_edid = bridge_ps8640_get_edid,
.post_power_on = bridge_ps8640_post_power_on,
};
if (i2c_read_field(bus, chip, addr, &msg, 0xFF, 0) < 0) {
printk(BIOS_ERR, "%s: Failed to read i2c(%u): addr(%u)\n",
__func__, bus, addr);
return -1;
}
msg &= ~mask;
msg |= val;
return i2c_write_field(bus, chip, addr, msg, 0xFF, 0);
}
void tps65132s_program_eeprom(void)
{
u8 value = 0;
u8 value1 = 0;
/* Initialize I2C6 for PMIC TPS65132 */
mtk_i2c_bus_init(PMIC_TPS65132_I2C, I2C_SPEED_FAST);
mdelay(10);
/* EN_PP6000_MIPI_DISP */
gpio_output(GPIO_EN_PP3300_DISP_X, 1);
/* EN_PP6000_MIPI_DISP_150MA */
gpio_output(GPIO_EN_PP3300_SDBRDG_X, 1);
mdelay(10);
i2c_read_field(PMIC_TPS65132_I2C, PMIC_TPS65132_SLAVE, 0x00, &value, 0xFF, 0);
i2c_read_field(PMIC_TPS65132_I2C, PMIC_TPS65132_SLAVE, 0x01, &value1, 0xFF, 0);
if (value != 0x14 || value1 != 0x14) {
printk(BIOS_INFO, "Set AVDD AVEE 6.0V to EEPROM Data in first time\n");
/* Set AVDD = 6.0V */
if (panel_pmic_reg_mask(PMIC_TPS65132_I2C, PMIC_TPS65132_SLAVE, 0x00, 0x14,
0x1F) < 0)
return;
/* Set AVEE = -6.0V */
if (panel_pmic_reg_mask(PMIC_TPS65132_I2C, PMIC_TPS65132_SLAVE, 0x01, 0x14,
0x1F) < 0)
return;
/* Set EEPROM Data */
if (panel_pmic_reg_mask(PMIC_TPS65132_I2C, PMIC_TPS65132_SLAVE, 0xFF, 0x80,
0xFC) < 0)
return;
mdelay(50);
}
/* EN_PP6000_MIPI_DISP */
gpio_output(GPIO_EN_PP3300_DISP_X, 0);
/* EN_PP6000_MIPI_DISP_150MA */
gpio_output(GPIO_EN_PP3300_SDBRDG_X, 0);
mdelay(5);
}
struct panel_description *get_panel_from_cbfs(struct panel_description *desc)
{
char cbfs_name[64];
static union {
u8 raw[4 * 1024];
struct panel_serializable_data s;
} buffer;
if (!desc->name)
return NULL;
snprintf(cbfs_name, sizeof(cbfs_name), "panel-%s", desc->name);
if (cbfs_load(cbfs_name, buffer.raw, sizeof(buffer)))
desc->s = &buffer.s;
else
printk(BIOS_ERR, "Missing %s in CBFS.\n", cbfs_name);
return desc->s ? desc : NULL;
}
static struct panel_description *get_active_panel(void)
{
if (CONFIG(BOARD_GOOGLE_KINGLER_COMMON))
if (CONFIG(BOARD_GOOGLE_STEELIX) && board_id() < 2)
return get_ps8640_description();
else
return get_anx7625_description();
else if (CONFIG(BOARD_GOOGLE_KRABBY_COMMON))
return get_ps8640_description();
else if (CONFIG(BOARD_GOOGLE_STARYU_COMMON))
return get_panel_description();
else
return NULL;
}
int configure_display(void)
{
struct edid edid;
const u8 i2c_bus = I2C0;
const struct edp_bridge *bridge = NULL;
uint32_t board_version = board_id();
const struct panel_description *panel = get_active_panel();
if (CONFIG(BOARD_GOOGLE_KINGLER_COMMON))
if (CONFIG(BOARD_GOOGLE_STEELIX) && board_version < 2)
bridge = &ps8640_bridge;
else
bridge = &anx7625_bridge;
else if (CONFIG(BOARD_GOOGLE_KRABBY_COMMON))
bridge = &ps8640_bridge;
if (!bridge)
if (!panel)
return -1;
printk(BIOS_INFO, "%s: Starting display init\n", __func__);
mtk_i2c_bus_init(i2c_bus, I2C_SPEED_FAST);
/* Set up backlight control pins as output pin and power-off by default */
backlight_control();
assert(bridge->power_on);
bridge->power_on();
assert(bridge->get_edid);
if (bridge->get_edid(i2c_bus, &edid) < 0) {
printk(BIOS_ERR, "%s: Failed to get edid\n", __func__);
return -1;
}
if (panel->power_on)
panel->power_on();
struct edid edid = panel->s->edid;
const char *name = edid.ascii_string;
if (name[0] == '\0')
name = "unknown name";
@ -176,19 +154,22 @@ int configure_display(void)
MIPI_DSI_MODE_LPM |
MIPI_DSI_MODE_EOT_PACKET);
if (mtk_dsi_init(mipi_dsi_flags, MIPI_DSI_FMT_RGB888, 4, &edid, NULL) < 0) {
if (mtk_dsi_init(mipi_dsi_flags, MIPI_DSI_FMT_RGB888, 4, &edid,
panel->s->init) < 0) {
printk(BIOS_ERR, "%s: Failed in DSI init\n", __func__);
return -1;
}
assert(bridge->post_power_on);
if (bridge->post_power_on(i2c_bus, &edid) < 0) {
if (panel->post_power_on && panel->post_power_on(BRIDGE_I2C, &edid) < 0) {
printk(BIOS_ERR, "%s: Failed to post power on bridge\n", __func__);
return -1;
}
mtk_ddp_mode_set(&edid);
fb_new_framebuffer_info_from_edid(&edid, (uintptr_t)0);
struct fb_info *info = fb_new_framebuffer_info_from_edid(&edid,
(uintptr_t)0);
if (info)
fb_set_orientation(info, panel->orientation);
return 0;
}

View File

@ -4,14 +4,37 @@
#define __MAINBOARD_GOOGLE_CORSOLA_DISPLAY_H__
#include <edid.h>
#include <mipi/panel.h>
#include <soc/i2c.h>
struct edp_bridge {
void (*power_on)(void);
int (*get_edid)(u8 i2c_bus, struct edid *edid);
#define BRIDGE_I2C I2C0
#define PMIC_TPS65132_I2C I2C6
#define PMIC_TPS65132_SLAVE 0x3E
struct panel_description {
void (*power_on)(void); /* Callback to turn on panel */
int (*post_power_on)(u8 i2c_bus, struct edid *edid);
const char *name; /* Panel name in CBFS */
struct panel_serializable_data *s;
enum lb_fb_orientation orientation;
};
int configure_display(void);
uint32_t panel_id(void);
/* Return the mipi panel description from given panel id */
struct panel_description *get_panel_description(void);
/* Return the ANX7625 bridge description */
struct panel_description *get_anx7625_description(void);
/* Return the PS8640 bridge description */
struct panel_description *get_ps8640_description(void);
/* Load panel serializable data from CBFS */
struct panel_description *get_panel_from_cbfs(struct panel_description *desc);
void tps65132s_program_eeprom(void);
int panel_pmic_reg_mask(u32 bus, u8 chip, u8 addr, u8 val, u8 mask);
#endif

View File

@ -0,0 +1,61 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#include <console/console.h>
#include <delay.h>
#include <drivers/analogix/anx7625/anx7625.h>
#include <edid.h>
#include <gpio.h>
#include <soc/i2c.h>
#include "display.h"
#include "gpio.h"
static void bridge_anx7625_power_on(void)
{
/* Turn on bridge */
gpio_output(GPIO_EDPBRDG_RST_L, 0);
gpio_output(GPIO_EN_PP1000_EDPBRDG, 1);
gpio_output(GPIO_EN_PP1800_EDPBRDG, 1);
gpio_output(GPIO_EN_PP3300_EDPBRDG, 1);
mdelay(14);
gpio_output(GPIO_EDPBRDG_PWREN, 1);
mdelay(80);
gpio_output(GPIO_EDPBRDG_RST_L, 1);
}
static int bridge_anx7625_get_edid(u8 i2c_bus, struct edid *edid)
{
if (anx7625_init(i2c_bus) < 0) {
printk(BIOS_ERR, "%s: Can't init ANX7625 bridge\n", __func__);
return -1;
}
if (anx7625_dp_get_edid(i2c_bus, edid) < 0) {
printk(BIOS_ERR, "%s: Can't get panel's edid\n", __func__);
return -1;
}
return 0;
}
static int bridge_anx7625_post_power_on(u8 i2c_bus, struct edid *edid)
{
return anx7625_dp_start(i2c_bus, edid);
}
static struct panel_serializable_data anx7625_data;
static struct panel_description anx7625_bridge = {
.s = &anx7625_data,
.post_power_on = bridge_anx7625_post_power_on,
.orientation = LB_FB_ORIENTATION_NORMAL,
};
struct panel_description *get_anx7625_description(void)
{
mtk_i2c_bus_init(BRIDGE_I2C, I2C_SPEED_FAST);
bridge_anx7625_power_on();
if (bridge_anx7625_get_edid(BRIDGE_I2C, &anx7625_bridge.s->edid) < 0) {
printk(BIOS_ERR, "Can't get panel's edid\n");
return NULL;
}
return &anx7625_bridge;
}

View File

@ -0,0 +1,79 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#include <console/console.h>
#include <delay.h>
#include <drivers/parade/ps8640/ps8640.h>
#include <edid.h>
#include <gpio.h>
#include <soc/i2c.h>
#include <soc/regulator.h>
#include "display.h"
#include "gpio.h"
static void bridge_ps8640_power_on(void)
{
/*
* PS8640 power-on sequence is described in chapter 14, PS8640_DS_V1.4_20200210.docx
* - set VDD12 to be 1.2V
* - delay 100us
* - set VDD33 to be 3.3V
* - pull hign PD#
* - pull down RST#
* - delay 2ms
* - pull high RST#
* - delay more than 50ms (55ms for margin)
* - pull down RST#
* - delay more than 50ms (55ms for margin)
* - pull high RST#
*/
/* Set VRF12 to 1.2V and VCN33 to 3.3V */
mainboard_set_regulator_voltage(MTK_REGULATOR_VRF12, 1200000);
udelay(100);
mainboard_set_regulator_voltage(MTK_REGULATOR_VCN33, 3300000);
udelay(200);
/* Turn on bridge */
gpio_output(GPIO_EDPBRDG_PWREN, 1);
gpio_output(GPIO_EDPBRDG_RST_L, 0);
mdelay(2);
gpio_output(GPIO_EDPBRDG_RST_L, 1);
mdelay(55);
gpio_output(GPIO_EDPBRDG_RST_L, 0);
mdelay(55);
gpio_output(GPIO_EDPBRDG_RST_L, 1);
}
static int bridge_ps8640_get_edid(u8 i2c_bus, struct edid *edid)
{
const u8 chip = 0x8;
if (ps8640_init(i2c_bus, chip) < 0) {
printk(BIOS_ERR, "%s: Can't init PS8640 bridge\n", __func__);
return -1;
}
if (ps8640_get_edid(i2c_bus, chip, edid) < 0) {
printk(BIOS_ERR, "%s: Can't get panel's edid\n", __func__);
return -1;
}
return 0;
}
static struct panel_serializable_data ps8640_data;
static struct panel_description ps8640_bridge = {
.s = &ps8640_data,
.orientation = LB_FB_ORIENTATION_NORMAL,
};
struct panel_description *get_ps8640_description(void)
{
mtk_i2c_bus_init(BRIDGE_I2C, I2C_SPEED_FAST);
bridge_ps8640_power_on();
if (bridge_ps8640_get_edid(BRIDGE_I2C, &ps8640_bridge.s->edid) < 0) {
printk(BIOS_ERR, "Can't get panel's edid\n");
return NULL;
}
return &ps8640_bridge;
}

View File

@ -0,0 +1,46 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#include <delay.h>
#include <gpio.h>
#include <soc/regulator.h>
#include "display.h"
#include "gpio.h"
static void mipi_panel_power_on(void)
{
tps65132s_program_eeprom();
mainboard_set_regulator_voltage(MTK_REGULATOR_VIO18, 1800000);
mdelay(1);
gpio_output(GPIO_EN_PP3300_DISP_X, 1);
gpio_output(GPIO_EN_PP3300_SDBRDG_X, 1);
mdelay(1);
/* DISP_RST_1V8_L */
gpio_output(GPIO_EDPBRDG_RST_L, 1);
mdelay(1);
gpio_output(GPIO_EDPBRDG_RST_L, 0);
udelay(20);
gpio_output(GPIO_EDPBRDG_RST_L, 1);
}
static struct panel_description starmie_panels[] = {
[8] = {
.power_on = mipi_panel_power_on,
.name = "STA_ILI9882T",
.orientation = LB_FB_ORIENTATION_LEFT_UP,
},
[10] = {
.power_on = mipi_panel_power_on,
.name = "STA_HIMAX83102_J02",
.orientation = LB_FB_ORIENTATION_LEFT_UP,
},
};
struct panel_description *get_panel_description(void)
{
uint32_t id = panel_id() & 0xF;
if (id >= ARRAY_SIZE(starmie_panels))
return NULL;
return get_panel_from_cbfs(&starmie_panels[id]);
}