summaryrefslogtreecommitdiff
path: root/src/mainboard/google/corsola/display.c
blob: bfe3f05ebb973a8c2a76115db64b1f33424b6969 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
/* SPDX-License-Identifier: GPL-2.0-only */

#include <assert.h>
#include <boardid.h>
#include <console/console.h>
#include <delay.h>
#include <drivers/analogix/anx7625/anx7625.h>
#include <drivers/parade/ps8640/ps8640.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 */
	gpio_output(GPIO_AP_EDP_BKLTEN, 0);
	gpio_output(GPIO_BL_PWM_1V8, 0);
	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,
};

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,
};

_Static_assert(CONFIG(BOARD_GOOGLE_KINGLER_COMMON) + CONFIG(BOARD_GOOGLE_KRABBY_COMMON) == 1,
	       "Exactly one of KINGLER and KRABBY must be set");

int configure_display(void)
{
	struct edid edid;
	const u8 i2c_bus = I2C0;
	const struct edp_bridge *bridge;
	uint32_t board_version = board_id();

	if (CONFIG(BOARD_GOOGLE_KINGLER_COMMON))
		if (CONFIG(BOARD_GOOGLE_STEELIX) && board_version < 2)
			bridge = &ps8640_bridge;
		else
			bridge = &anx7625_bridge;
	else /* BOARD_GOOGLE_KRABBY_COMMON */
		bridge = &ps8640_bridge;

	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;
	}

	const char *name = edid.ascii_string;
	if (name[0] == '\0')
		name = "unknown name";
	printk(BIOS_INFO, "%s: '%s %s' %dx%d@%dHz\n", __func__,
	       edid.manufacturer_name, name, edid.mode.ha, edid.mode.va,
	       edid.mode.refresh);

	mtcmos_display_power_on();
	mtcmos_protect_display_bus();

	edid_set_framebuffer_bits_per_pixel(&edid, 32, 0);
	mtk_ddp_init();
	u32 mipi_dsi_flags = (MIPI_DSI_MODE_VIDEO |
			      MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
			      MIPI_DSI_MODE_LPM |
			      MIPI_DSI_MODE_EOT_PACKET);

	if (mtk_dsi_init(mipi_dsi_flags, MIPI_DSI_FMT_RGB888, 4, &edid, NULL) < 0) {
		printk(BIOS_ERR, "%s: Failed in DSI init\n", __func__);
		return -1;
	}

	if (bridge->post_power_on(i2c_bus, &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);

	return 0;
}