mb/google/kukui: Initialize display

Many devices in Kukui family will be using MIPI panels, which needs
hard-coded EDID and initialization commands. And because each device may
have its own layout and ID, there should be very few devices sharing
same panel configuration. As a result, we want to put panel data (EDID
and init commands) into board-specific modules, provided by
`get_panel_description` function.

The panel numeric ID is identified by ADC 2, and is currently available
as higher 4 bits of sku_id(). After ID is retrieved, the
get_panel_description should return a reference to the EDID and table of
init commands. The default implementation is to simply return NULL, and
the data for real devices should be provided by panel_*.c in further commits.

BUG=b:80501386,b:117254947
BRANCH=none
TEST=boot correctly on Kukui

Change-Id: I19213aee1ac0f69f42e73be9e5ab72394f412a01
Signed-off-by: Hung-Te Lin <hungte@chromium.org>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/32511
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Julius Werner <jwerner@chromium.org>
diff --git a/src/mainboard/google/kukui/mainboard.c b/src/mainboard/google/kukui/mainboard.c
index 40b8a49..7bf2580 100644
--- a/src/mainboard/google/kukui/mainboard.c
+++ b/src/mainboard/google/kukui/mainboard.c
@@ -13,12 +13,22 @@
  * GNU General Public License for more details.
  */
 
+#include <boardid.h>
+#include <bootmode.h>
+#include <console/console.h>
+#include <delay.h>
 #include <device/device.h>
+#include <edid.h>
+#include <gpio.h>
+#include <soc/ddp.h>
+#include <soc/dsi.h>
 #include <soc/gpio.h>
 #include <soc/mmu_operations.h>
 #include <soc/mtcmos.h>
 #include <soc/usb.h>
 
+#include "panel.h"
+
 static void configure_emmc(void)
 {
 	const gpio_t emmc_pin[] = {
@@ -49,8 +59,99 @@
 	gpio_set_mode(GPIO(CAM_PDN0), PAD_CAM_PDN0_FUNC_I2S2_MCK);
 	gpio_set_mode(GPIO(EINT3), PAD_EINT3_FUNC_I2S3_DO);
 }
+
+/* Default implementation for boards without panels defined yet. */
+struct panel_description __weak *get_panel_description(int panel_id)
+{
+	printk(BIOS_ERR, "%s: ERROR: No panels defined for board: %s.\n",
+	       __func__, CONFIG_MAINBOARD_PART_NUMBER);
+	return NULL;
+}
+
+/* Set up backlight control pins as output pin and power-off by default */
+static void configure_panel_backlight(void)
+{
+	gpio_output(GPIO(PERIPHERAL_EN13), 0);
+	gpio_output(GPIO(DISP_PWM), 0);
+}
+
+static void power_on_panel(struct panel_description *panel)
+{
+	if (panel->power_on) {
+		panel->power_on();
+		return;
+	}
+
+	/* Default power sequence for most panels. */
+	gpio_output(GPIO_LCM_RST_1V8, 0);
+	gpio_output(GPIO_PPVARP_LCD_EN, 1);
+	gpio_output(GPIO_PPVARN_LCD_EN, 1);
+	gpio_output(GPIO_PP1800_LCM_EN, 1);
+	gpio_output(GPIO_PP3300_LCM_EN, 1);
+	mdelay(6);
+	gpio_output(GPIO_LCM_RST_1V8, 1);
+	mdelay(6);
+}
+
+static struct panel_description *get_active_panel(void)
+{
+	/* TODO(hungte) Create a dedicated panel_id() in board_id.c */
+	int panel_id = sku_id() >> 4;
+
+	struct panel_description *panel = get_panel_description(panel_id);
+	if (!panel) {
+		printk(BIOS_ERR, "%s: Panel %d is not supported.\n",
+		       __func__, panel_id);
+		return NULL;
+	}
+	const char *mode_name = panel->edid.mode.name;
+	const char *name = panel->edid.ascii_string;
+	if (name[0] == '\0')
+		name = "unknown name";
+	printk(BIOS_INFO, "%s: Found ID %d: '%s' %s\n", __func__,
+	       panel_id, name, mode_name ?  mode_name : "(unknown mode)");
+	return panel;
+}
+
+static bool configure_display(void)
+{
+	struct panel_description *panel = get_active_panel();
+	if (!panel)
+		return false;
+
+	mtcmos_display_power_on();
+	mtcmos_protect_display_bus();
+	configure_panel_backlight();
+	power_on_panel(panel);
+
+	struct edid *edid = &panel->edid;
+	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);
+	if (mtk_dsi_init(mipi_dsi_flags, MIPI_DSI_FMT_RGB888, 4, edid,
+			 panel->init) < 0) {
+		printk(BIOS_ERR, "%s: Failed in DSI init.\n", __func__);
+		return false;
+	}
+	mtk_ddp_mode_set(edid);
+	set_vbe_mode_info_valid(edid, 0);
+	set_vbe_framebuffer_orientation(panel->orientation);
+	return true;
+}
+
 static void mainboard_init(struct device *dev)
 {
+	if (display_init_required()) {
+		printk(BIOS_INFO, "%s: Starting display init.\n", __func__);
+		if (!configure_display())
+			printk(BIOS_ERR, "%s: Failed to init display.\n",
+			       __func__);
+	} else {
+		printk(BIOS_INFO, "%s: Skipped display init.\n", __func__);
+	}
+
 	configure_emmc();
 	configure_usb();
 	configure_audio();