// SPDX-License-Identifier: GPL-2.0+ /* * Hitachi TX10D07VM0BAA DSI panel driver * * Copyright (c) 2024 Svyatoslav Ryhel */ #include #include #include #include #include #include #include #include struct hitachi_tx10d07vm0baa_priv { struct udevice *avci; struct udevice *iovcc; struct udevice *backlight; struct gpio_desc reset_gpio; }; static struct display_timing default_timing = { .pixelclock.typ = 29816000, .hactive.typ = 480, .hfront_porch.typ = 10, .hback_porch.typ = 10, .hsync_len.typ = 10, .vactive.typ = 800, .vfront_porch.typ = 4, .vback_porch.typ = 4, .vsync_len.typ = 4, }; #define dsi_generic_write_seq(dsi, cmd, seq...) do { \ static const u8 b[] = { cmd, seq }; \ int ret; \ ret = mipi_dsi_dcs_write_buffer(dsi, b, ARRAY_SIZE(b)); \ if (ret < 0) \ return ret; \ } while (0) static int hitachi_tx10d07vm0baa_enable_backlight(struct udevice *dev) { struct mipi_dsi_panel_plat *plat = dev_get_plat(dev); struct mipi_dsi_device *dsi = plat->device; int ret; dsi_generic_write_seq(dsi, MIPI_DCS_SET_PARTIAL_AREA, 0x00, 0x00, 0x03, 0x1f); dsi_generic_write_seq(dsi, MIPI_DCS_SET_SCROLL_AREA, 0x00, 0x00, 0x03, 0x20, 0x00, 0x00); dsi_generic_write_seq(dsi, MIPI_DCS_SET_ADDRESS_MODE, 0x0a); dsi_generic_write_seq(dsi, MIPI_DCS_SET_SCROLL_START, 0x00, 0x00); ret = mipi_dsi_dcs_set_pixel_format(dsi, MIPI_DCS_PIXEL_FMT_24BIT); if (ret) { log_debug("%s: failed to set pixel format: %d\n", __func__, ret); return ret; } ret = mipi_dsi_dcs_set_tear_scanline(dsi, 0x00); if (ret) { log_debug("%s: failed to set tear scanline: %d\n", __func__, ret); return ret; } dsi_generic_write_seq(dsi, 0x71, 0x00); /* Ex_Vsync_en */ dsi_generic_write_seq(dsi, 0xb2, 0x00); /* VCSEL */ dsi_generic_write_seq(dsi, 0xb4, 0xaa); /* setvgmpm */ dsi_generic_write_seq(dsi, 0xb5, 0x33); /* rbias1 */ dsi_generic_write_seq(dsi, 0xb6, 0x03); /* rbias2 */ dsi_generic_write_seq(dsi, 0xb7, 0x1a, 0x33, 0x03, 0x03, 0x03, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x04, 0x00, 0x01, 0x01, 0x01); /* set_ddvdhp */ dsi_generic_write_seq(dsi, 0xb8, 0x1c, 0x53, 0x03, 0x03, 0x00, 0x01, 0x02, 0x00, 0x00, 0x04, 0x00, 0x01, 0x01); /* set_ddvdhm */ dsi_generic_write_seq(dsi, 0xb9, 0x0a, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x02, 0x01); /* set_vgh */ dsi_generic_write_seq(dsi, 0xba, 0x0f, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x02, 0x01); /* set_vgl */ dsi_generic_write_seq(dsi, 0xbb, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x01); /* set_vcl */ dsi_generic_write_seq(dsi, 0xc1, 0x01); /* number of lines */ dsi_generic_write_seq(dsi, 0xc2, 0x08); /* number of fp lines */ dsi_generic_write_seq(dsi, 0xc3, 0x04); /* gateset(1) */ dsi_generic_write_seq(dsi, 0xc4, 0x4c); /* 1h period */ dsi_generic_write_seq(dsi, 0xc5, 0x03); /* source precharge */ dsi_generic_write_seq(dsi, 0xc6, 0xc4, 0x04); /* source precharge timing */ dsi_generic_write_seq(dsi, 0xc7, 0x00); /* source level */ dsi_generic_write_seq(dsi, 0xc8, 0x02); /* number of bp lines */ dsi_generic_write_seq(dsi, 0xc9, 0x10); /* gateset(2) */ dsi_generic_write_seq(dsi, 0xca, 0x04, 0x04); /* gateset(3) */ dsi_generic_write_seq(dsi, 0xcb, 0x03); /* gateset(4) */ dsi_generic_write_seq(dsi, 0xcc, 0x12); /* gateset(5) */ dsi_generic_write_seq(dsi, 0xcd, 0x12); /* gateset(6) */ dsi_generic_write_seq(dsi, 0xce, 0x30); /* gateset(7) */ dsi_generic_write_seq(dsi, 0xcf, 0x30); /* gateset(8) */ dsi_generic_write_seq(dsi, 0xd0, 0x40); /* gateset(9) */ dsi_generic_write_seq(dsi, 0xd1, 0x22); /* flhw */ dsi_generic_write_seq(dsi, 0xd2, 0x22); /* vckhw */ dsi_generic_write_seq(dsi, 0xd3, 0x04); /* flt */ dsi_generic_write_seq(dsi, 0xd4, 0x14); /* tctrl */ dsi_generic_write_seq(dsi, 0xd6, 0x02); /* dotinv */ dsi_generic_write_seq(dsi, 0xd7, 0x00); /* on/off sequence period */ dsi_generic_write_seq(dsi, 0xd8, 0x01, 0x05, 0x06, 0x0d, 0x18, 0x09, 0x22, 0x23, 0x00); /* ponseqa */ dsi_generic_write_seq(dsi, 0xd9, 0x24, 0x01); /* ponseqb */ dsi_generic_write_seq(dsi, 0xde, 0x09, 0x0f, 0x21, 0x12, 0x04); /* ponseqc */ dsi_generic_write_seq(dsi, 0xdf, 0x02, 0x06, 0x06, 0x06, 0x06, 0x00); /* pofseqa */ dsi_generic_write_seq(dsi, 0xe0, 0x01); /* pofseqb */ ret = mipi_dsi_dcs_set_display_brightness(dsi, 0xff); if (ret) { log_debug("%s: failed to set display brightness: %d\n", __func__, ret); return ret; } dsi_generic_write_seq(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x40); dsi_generic_write_seq(dsi, 0xe2, 0x00, 0x00); /* cabc pwm */ dsi_generic_write_seq(dsi, 0xe3, 0x03); /* cabc */ dsi_generic_write_seq(dsi, 0xe4, 0x66, 0x7b, 0x90, 0xa5, 0xbb, 0xc7, 0xe1, 0xe5); /* cabc brightness */ dsi_generic_write_seq(dsi, 0xe5, 0xc5, 0xc5, 0xc9, 0xc9, 0xd1, 0xe1, 0xf1, 0xfe); /* cabc brightness */ dsi_generic_write_seq(dsi, 0xe7, 0x2a); /* cabc */ dsi_generic_write_seq(dsi, 0xe8, 0x00); /* brt_rev */ dsi_generic_write_seq(dsi, 0xe9, 0x00); /* tefreq */ dsi_generic_write_seq(dsi, 0xea, 0x01); /* high speed ram */ dsi_generic_write_seq(dsi, 0xeb, 0x00, 0x33, 0x0e, 0x15, 0xb7, 0x78, 0x88, 0x0f); /* gamma setting r pos */ dsi_generic_write_seq(dsi, 0xec, 0x00, 0x33, 0x0e, 0x15, 0xb7, 0x78, 0x88, 0x0f); /* gamma setting r neg */ dsi_generic_write_seq(dsi, 0xed, 0x00, 0x33, 0x0e, 0x15, 0xb7, 0x78, 0x88, 0x0f); /* gamma setting g pos */ dsi_generic_write_seq(dsi, 0xee, 0x00, 0x33, 0x0e, 0x15, 0xb7, 0x78, 0x88, 0x0f); /* gamma setting g neg */ dsi_generic_write_seq(dsi, 0xef, 0x00, 0x33, 0x0e, 0x15, 0xb7, 0x78, 0x88, 0x0f); /* gamma setting b pos */ dsi_generic_write_seq(dsi, 0xf0, 0x00, 0x33, 0x0e, 0x15, 0xb7, 0x78, 0x88, 0x0f); /* gamma setting b neg */ ret = mipi_dsi_dcs_exit_sleep_mode(dsi); if (ret) { log_debug("%s: failed to exit sleep mode: %d\n", __func__, ret); return ret; } mdelay(110); ret = mipi_dsi_dcs_set_display_on(dsi); if (ret) { log_debug("%s: failed to set display on: %d\n", __func__, ret); return ret; } return 0; } static int hitachi_tx10d07vm0baa_set_backlight(struct udevice *dev, int percent) { struct hitachi_tx10d07vm0baa_priv *priv = dev_get_priv(dev); int ret; ret = backlight_enable(priv->backlight); if (ret) return ret; return backlight_set_brightness(priv->backlight, percent); } static int hitachi_tx10d07vm0baa_timings(struct udevice *dev, struct display_timing *timing) { memcpy(timing, &default_timing, sizeof(*timing)); return 0; } static int hitachi_tx10d07vm0baa_of_to_plat(struct udevice *dev) { struct hitachi_tx10d07vm0baa_priv *priv = dev_get_priv(dev); int ret; ret = uclass_get_device_by_phandle(UCLASS_PANEL_BACKLIGHT, dev, "backlight", &priv->backlight); if (ret) { log_debug("%s: cannot get backlight: ret = %d\n", __func__, ret); return ret; } ret = device_get_supply_regulator(dev, "avci-supply", &priv->avci); if (ret) { log_debug("%s: cannot get avci-supply: ret = %d\n", __func__, ret); return ret; } ret = device_get_supply_regulator(dev, "iovcc-supply", &priv->iovcc); if (ret) { log_debug("%s: cannot get iovcc-supply: ret = %d\n", __func__, ret); return ret; } ret = gpio_request_by_name(dev, "reset-gpios", 0, &priv->reset_gpio, GPIOD_IS_OUT); if (ret) { log_debug("%s: cannot decode reset-gpios (%d)\n", __func__, ret); return ret; } return 0; } static int hitachi_tx10d07vm0baa_hw_init(struct udevice *dev) { struct hitachi_tx10d07vm0baa_priv *priv = dev_get_priv(dev); int ret; ret = dm_gpio_set_value(&priv->reset_gpio, 1); if (ret) { log_debug("%s: error entering reset (%d)\n", __func__, ret); return ret; } ret = regulator_set_enable_if_allowed(priv->iovcc, 1); if (ret) { log_debug("%s: enabling iovcc-supply failed (%d)\n", __func__, ret); return ret; } ret = regulator_set_enable_if_allowed(priv->avci, 1); if (ret) { log_debug("%s: enabling avci-supply failed (%d)\n", __func__, ret); return ret; } mdelay(25); ret = dm_gpio_set_value(&priv->reset_gpio, 0); if (ret) { log_debug("%s: error exiting reset (%d)\n", __func__, ret); return ret; } mdelay(5); return 0; } static int hitachi_tx10d07vm0baa_probe(struct udevice *dev) { struct mipi_dsi_panel_plat *plat = dev_get_plat(dev); /* fill characteristics of DSI data link */ plat->lanes = 2; plat->format = MIPI_DSI_FMT_RGB888; plat->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM; return hitachi_tx10d07vm0baa_hw_init(dev); } static const struct panel_ops hitachi_tx10d07vm0baa_ops = { .enable_backlight = hitachi_tx10d07vm0baa_enable_backlight, .set_backlight = hitachi_tx10d07vm0baa_set_backlight, .get_display_timing = hitachi_tx10d07vm0baa_timings, }; static const struct udevice_id hitachi_tx10d07vm0baa_ids[] = { { .compatible = "hit,tx10d07vm0baa" }, { } }; U_BOOT_DRIVER(hitachi_tx10d07vm0baa) = { .name = "hitachi_tx10d07vm0baa", .id = UCLASS_PANEL, .of_match = hitachi_tx10d07vm0baa_ids, .ops = &hitachi_tx10d07vm0baa_ops, .of_to_plat = hitachi_tx10d07vm0baa_of_to_plat, .probe = hitachi_tx10d07vm0baa_probe, .plat_auto = sizeof(struct mipi_dsi_panel_plat), .priv_auto = sizeof(struct hitachi_tx10d07vm0baa_priv), };