diff options
Diffstat (limited to 'target/linux/bcm27xx/patches-5.15/950-0693-drm-panel-Add-panel-driver-for-TDO-Y17B-based-panels.patch')
-rw-r--r-- | target/linux/bcm27xx/patches-5.15/950-0693-drm-panel-Add-panel-driver-for-TDO-Y17B-based-panels.patch | 331 |
1 files changed, 331 insertions, 0 deletions
diff --git a/target/linux/bcm27xx/patches-5.15/950-0693-drm-panel-Add-panel-driver-for-TDO-Y17B-based-panels.patch b/target/linux/bcm27xx/patches-5.15/950-0693-drm-panel-Add-panel-driver-for-TDO-Y17B-based-panels.patch new file mode 100644 index 0000000000..4c96b773c7 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.15/950-0693-drm-panel-Add-panel-driver-for-TDO-Y17B-based-panels.patch @@ -0,0 +1,331 @@ +From f493a1250d55c4a61d94f631757186e8beef9d90 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson <dave.stevenson@raspberrypi.com> +Date: Wed, 26 Jan 2022 16:02:31 +0000 +Subject: [PATCH] drm/panel: Add panel driver for TDO Y17B based panels + +The Top DisplayOptoelectronics (TDO) T17B driver chip is used +in the TL040HDS20CT panel (found in the Pimoroni HyperPixel4 +Square display) and potentially other displays. +The driver chip supports SPI for configuration and DPI +video data. + +Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> +--- + drivers/gpu/drm/panel/Kconfig | 11 + + drivers/gpu/drm/panel/Makefile | 1 + + drivers/gpu/drm/panel/panel-tdo-y17p.c | 279 +++++++++++++++++++++++++ + 3 files changed, 291 insertions(+) + create mode 100644 drivers/gpu/drm/panel/panel-tdo-y17p.c + +--- a/drivers/gpu/drm/panel/Kconfig ++++ b/drivers/gpu/drm/panel/Kconfig +@@ -560,6 +560,17 @@ config DRM_PANEL_SONY_ACX565AKM + Say Y here if you want to enable support for the Sony ACX565AKM + 800x600 3.5" panel (found on the Nokia N900). + ++config DRM_PANEL_TPO_Y17P ++ tristate "TDO Y17P-based panels" ++ depends on OF && SPI ++ depends on DRM_KMS_HELPER ++ depends on DRM_KMS_CMA_HELPER ++ depends on BACKLIGHT_CLASS_DEVICE ++ select DRM_MIPI_DBI ++ help ++ Say Y if you want to enable support for panels based on the ++ TDO Y17P controller. ++ + config DRM_PANEL_TDO_TL070WSH30 + tristate "TDO TL070WSH30 DSI panel" + depends on OF +--- a/drivers/gpu/drm/panel/Makefile ++++ b/drivers/gpu/drm/panel/Makefile +@@ -58,6 +58,7 @@ obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7789V) + obj-$(CONFIG_DRM_PANEL_SONY_ACX424AKP) += panel-sony-acx424akp.o + obj-$(CONFIG_DRM_PANEL_SONY_ACX565AKM) += panel-sony-acx565akm.o + obj-$(CONFIG_DRM_PANEL_TDO_TL070WSH30) += panel-tdo-tl070wsh30.o ++obj-$(CONFIG_DRM_PANEL_TPO_Y17P) += panel-tdo-y17p.o + obj-$(CONFIG_DRM_PANEL_TPO_TD028TTEC1) += panel-tpo-td028ttec1.o + obj-$(CONFIG_DRM_PANEL_TPO_TD043MTEA1) += panel-tpo-td043mtea1.o + obj-$(CONFIG_DRM_PANEL_TPO_TPG110) += panel-tpo-tpg110.o +--- /dev/null ++++ b/drivers/gpu/drm/panel/panel-tdo-y17p.c +@@ -0,0 +1,279 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * TDO Y17P TFT LCD drm_panel driver. ++ * ++ * SPI configured DPI display controller ++ * Copyright (C) 2022 Raspberry Pi Ltd ++ * ++ * Derived from drivers/drm/gpu/panel/panel-sitronix-st7789v.c ++ * Copyright (C) 2017 Free Electrons ++ */ ++ ++#include <drm/drm_modes.h> ++#include <drm/drm_panel.h> ++ ++#include <linux/bitops.h> ++#include <linux/gpio/consumer.h> ++#include <linux/media-bus-format.h> ++#include <linux/module.h> ++#include <linux/of_device.h> ++#include <linux/regmap.h> ++#include <linux/regulator/consumer.h> ++#include <linux/spi/spi.h> ++ ++#include <video/mipi_display.h> ++#include <video/of_videomode.h> ++#include <video/videomode.h> ++ ++struct tdo_y17p { ++ struct drm_panel panel; ++ struct spi_device *spi; ++ struct gpio_desc *reset; ++ struct regulator *power; ++ u32 bus_format; ++}; ++ ++static const u16 panel_init[] = { ++ 0x0ff, 0x1ff, 0x198, 0x106, 0x104, 0x101, 0x008, 0x110, ++ 0x021, 0x109, 0x030, 0x102, 0x031, 0x100, 0x040, 0x110, ++ 0x041, 0x155, 0x042, 0x102, 0x043, 0x109, 0x044, 0x107, ++ 0x050, 0x178, 0x051, 0x178, 0x052, 0x100, 0x053, 0x16d, ++ 0x060, 0x107, 0x061, 0x100, 0x062, 0x108, 0x063, 0x100, ++ 0x0a0, 0x100, 0x0a1, 0x107, 0x0a2, 0x10c, 0x0a3, 0x10b, ++ 0x0a4, 0x103, 0x0a5, 0x107, 0x0a6, 0x106, 0x0a7, 0x104, ++ 0x0a8, 0x108, 0x0a9, 0x10c, 0x0aa, 0x113, 0x0ab, 0x106, ++ 0x0ac, 0x10d, 0x0ad, 0x119, 0x0ae, 0x110, 0x0af, 0x100, ++ 0x0c0, 0x100, 0x0c1, 0x107, 0x0c2, 0x10c, 0x0c3, 0x10b, ++ 0x0c4, 0x103, 0x0c5, 0x107, 0x0c6, 0x107, 0x0c7, 0x104, ++ 0x0c8, 0x108, 0x0c9, 0x10c, 0x0ca, 0x113, 0x0cb, 0x106, ++ 0x0cc, 0x10d, 0x0cd, 0x118, 0x0ce, 0x110, 0x0cf, 0x100, ++ 0x0ff, 0x1ff, 0x198, 0x106, 0x104, 0x106, 0x000, 0x120, ++ 0x001, 0x10a, 0x002, 0x100, 0x003, 0x100, 0x004, 0x101, ++ 0x005, 0x101, 0x006, 0x198, 0x007, 0x106, 0x008, 0x101, ++ 0x009, 0x180, 0x00a, 0x100, 0x00b, 0x100, 0x00c, 0x101, ++ 0x00d, 0x101, 0x00e, 0x100, 0x00f, 0x100, 0x010, 0x1f0, ++ 0x011, 0x1f4, 0x012, 0x101, 0x013, 0x100, 0x014, 0x100, ++ 0x015, 0x1c0, 0x016, 0x108, 0x017, 0x100, 0x018, 0x100, ++ 0x019, 0x100, 0x01a, 0x100, 0x01b, 0x100, 0x01c, 0x100, ++ 0x01d, 0x100, 0x020, 0x101, 0x021, 0x123, 0x022, 0x145, ++ 0x023, 0x167, 0x024, 0x101, 0x025, 0x123, 0x026, 0x145, ++ 0x027, 0x167, 0x030, 0x111, 0x031, 0x111, 0x032, 0x100, ++ 0x033, 0x1ee, 0x034, 0x1ff, 0x035, 0x1bb, 0x036, 0x1aa, ++ 0x037, 0x1dd, 0x038, 0x1cc, 0x039, 0x166, 0x03a, 0x177, ++ 0x03b, 0x122, 0x03c, 0x122, 0x03d, 0x122, 0x03e, 0x122, ++ 0x03f, 0x122, 0x040, 0x122, 0x052, 0x110, 0x053, 0x110, ++ 0x0ff, 0x1ff, 0x198, 0x106, 0x104, 0x107, 0x018, 0x11d, ++ 0x017, 0x122, 0x002, 0x177, 0x026, 0x1b2, 0x0e1, 0x179, ++ 0x0ff, 0x1ff, 0x198, 0x106, 0x104, 0x100, 0x03a, 0x160, ++ 0x035, 0x100, 0x011, 0x100, ++}; ++ ++#define NUM_INIT_REGS ARRAY_SIZE(panel_init) ++ ++static inline struct tdo_y17p *panel_to_tdo_y17p(struct drm_panel *panel) ++{ ++ return container_of(panel, struct tdo_y17p, panel); ++} ++ ++static int tdo_y17p_write_msg(struct tdo_y17p *ctx, const u16 *msg, unsigned int len) ++{ ++ struct spi_transfer xfer = { }; ++ struct spi_message spi; ++ ++ spi_message_init(&spi); ++ ++ xfer.tx_buf = msg; ++ xfer.bits_per_word = 9; ++ xfer.len = sizeof(u16) * len; ++ ++ spi_message_add_tail(&xfer, &spi); ++ return spi_sync(ctx->spi, &spi); ++} ++ ++static const struct drm_display_mode tdo_y17pe_720x720_mode = { ++ .clock = 36720, ++ .hdisplay = 720, ++ .hsync_start = 720 + 20, ++ .hsync_end = 720 + 20 + 20, ++ .htotal = 720 + 20 + 20 + 40, ++ .vdisplay = 720, ++ .vsync_start = 720 + 15, ++ .vsync_end = 720 + 15 + 15, ++ .vtotal = 720 + 15 + 15 + 15, ++ .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC, ++}; ++ ++static int tdo_y17p_get_modes(struct drm_panel *panel, ++ struct drm_connector *connector) ++{ ++ struct tdo_y17p *ctx = panel_to_tdo_y17p(panel); ++ struct drm_display_mode *mode; ++ ++ mode = drm_mode_duplicate(connector->dev, &tdo_y17pe_720x720_mode); ++ if (!mode) { ++ dev_err(panel->dev, "failed to add mode %ux%ux@%u\n", ++ tdo_y17pe_720x720_mode.hdisplay, ++ tdo_y17pe_720x720_mode.vdisplay, ++ drm_mode_vrefresh(&tdo_y17pe_720x720_mode)); ++ return -ENOMEM; ++ } ++ ++ drm_mode_set_name(mode); ++ ++ mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; ++ drm_mode_probed_add(connector, mode); ++ ++ connector->display_info.width_mm = 72; ++ connector->display_info.height_mm = 72; ++ drm_display_info_set_bus_formats(&connector->display_info, ++ &ctx->bus_format, 1); ++ connector->display_info.bus_flags = 0; ++ ++ return 1; ++} ++ ++static int tdo_y17p_prepare(struct drm_panel *panel) ++{ ++ struct tdo_y17p *ctx = panel_to_tdo_y17p(panel); ++ int ret; ++ ++ ret = regulator_enable(ctx->power); ++ if (ret) ++ return ret; ++ ++ ret = tdo_y17p_write_msg(ctx, panel_init, NUM_INIT_REGS); ++ ++ msleep(120); ++ ++ return ret; ++} ++ ++static int tdo_y17p_enable(struct drm_panel *panel) ++{ ++ struct tdo_y17p *ctx = panel_to_tdo_y17p(panel); ++ const u16 msg[] = { MIPI_DCS_SET_DISPLAY_ON }; ++ int ret; ++ ++ ret = tdo_y17p_write_msg(ctx, msg, ARRAY_SIZE(msg)); ++ ++ return ret; ++} ++ ++static int tdo_y17p_disable(struct drm_panel *panel) ++{ ++ struct tdo_y17p *ctx = panel_to_tdo_y17p(panel); ++ const u16 msg[] = { MIPI_DCS_SET_DISPLAY_OFF }; ++ int ret; ++ ++ ret = tdo_y17p_write_msg(ctx, msg, ARRAY_SIZE(msg)); ++ ++ return ret; ++} ++ ++static int tdo_y17p_unprepare(struct drm_panel *panel) ++{ ++ struct tdo_y17p *ctx = panel_to_tdo_y17p(panel); ++ const u16 msg[] = { MIPI_DCS_ENTER_SLEEP_MODE }; ++ int ret; ++ ++ ret = tdo_y17p_write_msg(ctx, msg, ARRAY_SIZE(msg)); ++ ++ return ret; ++} ++ ++static const struct drm_panel_funcs tdo_y17p_drm_funcs = { ++ .disable = tdo_y17p_disable, ++ .enable = tdo_y17p_enable, ++ .get_modes = tdo_y17p_get_modes, ++ .prepare = tdo_y17p_prepare, ++ .unprepare = tdo_y17p_unprepare, ++}; ++ ++static const struct of_device_id tdo_y17p_of_match[] = { ++ { .compatible = "tdo,tl040hds20ct", ++ .data = (void *)MEDIA_BUS_FMT_BGR888_1X24, ++ }, { ++ .compatible = "pimoroni,hyperpixel4square", ++ .data = (void *)MEDIA_BUS_FMT_BGR666_1X24_CPADHI, ++ }, { ++ .compatible = "tdo,y17p", ++ .data = (void *)MEDIA_BUS_FMT_BGR888_1X24, ++ }, { ++ /* sentinel */ ++ } ++}; ++MODULE_DEVICE_TABLE(of, tdo_y17p_of_match); ++ ++static int tdo_y17p_probe(struct spi_device *spi) ++{ ++ const struct of_device_id *id; ++ struct tdo_y17p *ctx; ++ int ret; ++ ++ ctx = devm_kzalloc(&spi->dev, sizeof(*ctx), GFP_KERNEL); ++ if (!ctx) ++ return -ENOMEM; ++ ++ id = of_match_node(tdo_y17p_of_match, spi->dev.of_node); ++ if (!id) ++ return -ENODEV; ++ ++ ctx->bus_format = (u32)id->data; ++ ++ spi_set_drvdata(spi, ctx); ++ ctx->spi = spi; ++ ++ drm_panel_init(&ctx->panel, &spi->dev, &tdo_y17p_drm_funcs, ++ DRM_MODE_CONNECTOR_DPI); ++ ++ ctx->power = devm_regulator_get(&spi->dev, "power"); ++ if (IS_ERR(ctx->power)) ++ return PTR_ERR(ctx->power); ++ ++ ctx->reset = devm_gpiod_get_optional(&spi->dev, "reset", GPIOD_OUT_LOW); ++ if (IS_ERR(ctx->reset)) { ++ dev_err(&spi->dev, "Couldn't get our reset line\n"); ++ return PTR_ERR(ctx->reset); ++ } ++ ++ ret = drm_panel_of_backlight(&ctx->panel); ++ if (ret) ++ return ret; ++ ++ drm_panel_add(&ctx->panel); ++ ++ return 0; ++} ++ ++static int tdo_y17p_remove(struct spi_device *spi) ++{ ++ struct tdo_y17p *ctx = spi_get_drvdata(spi); ++ ++ drm_panel_remove(&ctx->panel); ++ ++ return 0; ++} ++ ++static const struct spi_device_id tdo_y17p_ids[] = { ++ { "tl040hds20ct", 0 }, ++ { "hyperpixel4square", 0 }, ++ { "y17p", 0 }, ++ { /* sentinel */ } ++}; ++ ++MODULE_DEVICE_TABLE(spi, tdo_y17p_ids); ++ ++static struct spi_driver tdo_y17p_driver = { ++ .probe = tdo_y17p_probe, ++ .remove = tdo_y17p_remove, ++ .driver = { ++ .name = "tdo_y17p", ++ .of_match_table = tdo_y17p_of_match, ++ }, ++ .id_table = tdo_y17p_ids, ++}; ++module_spi_driver(tdo_y17p_driver); ++ ++MODULE_AUTHOR("Dave Stevenson <dave.stevenson@raspberrypi.com>"); ++MODULE_DESCRIPTION("TDO Y17P LCD panel driver"); ++MODULE_LICENSE("GPL v2"); |