diff options
Diffstat (limited to 'SOURCES/0001-drm-panel-rocktech-jh057n00900-Rename-the-driver-to-.patch')
-rw-r--r-- | SOURCES/0001-drm-panel-rocktech-jh057n00900-Rename-the-driver-to-.patch | 948 |
1 files changed, 948 insertions, 0 deletions
diff --git a/SOURCES/0001-drm-panel-rocktech-jh057n00900-Rename-the-driver-to-.patch b/SOURCES/0001-drm-panel-rocktech-jh057n00900-Rename-the-driver-to-.patch new file mode 100644 index 0000000..d78bf56 --- /dev/null +++ b/SOURCES/0001-drm-panel-rocktech-jh057n00900-Rename-the-driver-to-.patch @@ -0,0 +1,948 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman <megous@megous.com> +Date: Fri, 26 Jun 2020 02:55:52 +0200 +Subject: [PATCH] drm/panel: rocktech-jh057n00900: Rename the driver to st7703 + +This rename is done so that the driver matches the name of the +display controller and in preparation for adding support for more +panels to the driver. + +This is just a basic file rename, with no code changes. + +Signed-off-by: Ondrej Jirman <megous@megous.com> +--- + drivers/gpu/drm/panel/Kconfig | 26 +- + drivers/gpu/drm/panel/Makefile | 2 +- + .../drm/panel/panel-rocktech-jh057n00900.c | 424 ------------------ + drivers/gpu/drm/panel/panel-sitronix-st7703.c | 424 ++++++++++++++++++ + 4 files changed, 438 insertions(+), 438 deletions(-) + delete mode 100644 drivers/gpu/drm/panel/panel-rocktech-jh057n00900.c + create mode 100644 drivers/gpu/drm/panel/panel-sitronix-st7703.c + +diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig +index 39055c1f0e2f..de2f2a452be5 100644 +--- a/drivers/gpu/drm/panel/Kconfig ++++ b/drivers/gpu/drm/panel/Kconfig +@@ -283,19 +283,6 @@ config DRM_PANEL_RAYDIUM_RM68200 + Say Y here if you want to enable support for Raydium RM68200 + 720x1280 DSI video mode panel. + +-config DRM_PANEL_ROCKTECH_JH057N00900 +- tristate "Rocktech JH057N00900 MIPI touchscreen panel" +- depends on OF +- depends on DRM_MIPI_DSI +- depends on BACKLIGHT_CLASS_DEVICE +- help +- Say Y here if you want to enable support for Rocktech JH057N00900 +- MIPI DSI panel as e.g. used in the Librem 5 devkit. It has a +- resolution of 720x1440 pixels, a built in backlight and touch +- controller. +- Touch input support is provided by the goodix driver and needs to be +- selected separately. +- + config DRM_PANEL_RONBO_RB070D30 + tristate "Ronbo Electronics RB070D30 panel" + depends on OF +@@ -395,6 +382,19 @@ config DRM_PANEL_SITRONIX_ST7701 + ST7701 controller for 480X864 LCD panels with MIPI/RGB/SPI + system interfaces. + ++config DRM_PANEL_SITRONIX_ST7703 ++ tristate "Sitronix ST7703 based MIPI touchscreen panels" ++ depends on OF ++ depends on DRM_MIPI_DSI ++ depends on BACKLIGHT_CLASS_DEVICE ++ help ++ Say Y here if you want to enable support for Sitronix ST7703 based ++ panels, souch as Rocktech JH057N00900 MIPI DSI panel as e.g. used in ++ the Librem 5 devkit. It has a resolution of 720x1440 pixels, a built ++ in backlight and touch controller. ++ Touch input support is provided by the goodix driver and needs to be ++ selected separately. ++ + config DRM_PANEL_SITRONIX_ST7789V + tristate "Sitronix ST7789V panel" + depends on OF && SPI +diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile +index de74f282c433..e45ceac6286f 100644 +--- a/drivers/gpu/drm/panel/Makefile ++++ b/drivers/gpu/drm/panel/Makefile +@@ -27,7 +27,6 @@ obj-$(CONFIG_DRM_PANEL_PANASONIC_VVX10F034N00) += panel-panasonic-vvx10f034n00.o + obj-$(CONFIG_DRM_PANEL_RASPBERRYPI_TOUCHSCREEN) += panel-raspberrypi-touchscreen.o + obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM67191) += panel-raydium-rm67191.o + obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM68200) += panel-raydium-rm68200.o +-obj-$(CONFIG_DRM_PANEL_ROCKTECH_JH057N00900) += panel-rocktech-jh057n00900.o + obj-$(CONFIG_DRM_PANEL_RONBO_RB070D30) += panel-ronbo-rb070d30.o + obj-$(CONFIG_DRM_PANEL_SAMSUNG_LD9040) += panel-samsung-ld9040.o + obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6D16D0) += panel-samsung-s6d16d0.o +@@ -41,6 +40,7 @@ obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o + obj-$(CONFIG_DRM_PANEL_SHARP_LS037V7DW01) += panel-sharp-ls037v7dw01.o + obj-$(CONFIG_DRM_PANEL_SHARP_LS043T1LE01) += panel-sharp-ls043t1le01.o + obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7701) += panel-sitronix-st7701.o ++obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7703) += panel-sitronix-st7703.o + obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7789V) += panel-sitronix-st7789v.o + obj-$(CONFIG_DRM_PANEL_SONY_ACX424AKP) += panel-sony-acx424akp.o + obj-$(CONFIG_DRM_PANEL_SONY_ACX565AKM) += panel-sony-acx565akm.o +diff --git a/drivers/gpu/drm/panel/panel-rocktech-jh057n00900.c b/drivers/gpu/drm/panel/panel-rocktech-jh057n00900.c +deleted file mode 100644 +index 38ff742bc120..000000000000 +--- a/drivers/gpu/drm/panel/panel-rocktech-jh057n00900.c ++++ /dev/null +@@ -1,424 +0,0 @@ +-// SPDX-License-Identifier: GPL-2.0 +-/* +- * Rockteck jh057n00900 5.5" MIPI-DSI panel driver +- * +- * Copyright (C) Purism SPC 2019 +- */ +- +-#include <linux/debugfs.h> +-#include <linux/delay.h> +-#include <linux/gpio/consumer.h> +-#include <linux/media-bus-format.h> +-#include <linux/mod_devicetable.h> +-#include <linux/module.h> +-#include <linux/regulator/consumer.h> +- +-#include <video/display_timing.h> +-#include <video/mipi_display.h> +- +-#include <drm/drm_mipi_dsi.h> +-#include <drm/drm_modes.h> +-#include <drm/drm_panel.h> +-#include <drm/drm_print.h> +- +-#define DRV_NAME "panel-rocktech-jh057n00900" +- +-/* Manufacturer specific Commands send via DSI */ +-#define ST7703_CMD_ALL_PIXEL_OFF 0x22 +-#define ST7703_CMD_ALL_PIXEL_ON 0x23 +-#define ST7703_CMD_SETDISP 0xB2 +-#define ST7703_CMD_SETRGBIF 0xB3 +-#define ST7703_CMD_SETCYC 0xB4 +-#define ST7703_CMD_SETBGP 0xB5 +-#define ST7703_CMD_SETVCOM 0xB6 +-#define ST7703_CMD_SETOTP 0xB7 +-#define ST7703_CMD_SETPOWER_EXT 0xB8 +-#define ST7703_CMD_SETEXTC 0xB9 +-#define ST7703_CMD_SETMIPI 0xBA +-#define ST7703_CMD_SETVDC 0xBC +-#define ST7703_CMD_UNKNOWN0 0xBF +-#define ST7703_CMD_SETSCR 0xC0 +-#define ST7703_CMD_SETPOWER 0xC1 +-#define ST7703_CMD_SETPANEL 0xCC +-#define ST7703_CMD_SETGAMMA 0xE0 +-#define ST7703_CMD_SETEQ 0xE3 +-#define ST7703_CMD_SETGIP1 0xE9 +-#define ST7703_CMD_SETGIP2 0xEA +- +-struct jh057n { +- struct device *dev; +- struct drm_panel panel; +- struct gpio_desc *reset_gpio; +- struct regulator *vcc; +- struct regulator *iovcc; +- bool prepared; +- +- struct dentry *debugfs; +-}; +- +-static inline struct jh057n *panel_to_jh057n(struct drm_panel *panel) +-{ +- return container_of(panel, struct jh057n, panel); +-} +- +-#define dsi_generic_write_seq(dsi, seq...) do { \ +- static const u8 d[] = { seq }; \ +- int ret; \ +- ret = mipi_dsi_generic_write(dsi, d, ARRAY_SIZE(d)); \ +- if (ret < 0) \ +- return ret; \ +- } while (0) +- +-static int jh057n_init_sequence(struct jh057n *ctx) +-{ +- struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); +- struct device *dev = ctx->dev; +- int ret; +- +- /* +- * Init sequence was supplied by the panel vendor. Most of the commands +- * resemble the ST7703 but the number of parameters often don't match +- * so it's likely a clone. +- */ +- dsi_generic_write_seq(dsi, ST7703_CMD_SETEXTC, +- 0xF1, 0x12, 0x83); +- dsi_generic_write_seq(dsi, ST7703_CMD_SETRGBIF, +- 0x10, 0x10, 0x05, 0x05, 0x03, 0xFF, 0x00, 0x00, +- 0x00, 0x00); +- dsi_generic_write_seq(dsi, ST7703_CMD_SETSCR, +- 0x73, 0x73, 0x50, 0x50, 0x00, 0x00, 0x08, 0x70, +- 0x00); +- dsi_generic_write_seq(dsi, ST7703_CMD_SETVDC, 0x4E); +- dsi_generic_write_seq(dsi, ST7703_CMD_SETPANEL, 0x0B); +- dsi_generic_write_seq(dsi, ST7703_CMD_SETCYC, 0x80); +- dsi_generic_write_seq(dsi, ST7703_CMD_SETDISP, 0xF0, 0x12, 0x30); +- dsi_generic_write_seq(dsi, ST7703_CMD_SETEQ, +- 0x07, 0x07, 0x0B, 0x0B, 0x03, 0x0B, 0x00, 0x00, +- 0x00, 0x00, 0xFF, 0x00, 0xC0, 0x10); +- dsi_generic_write_seq(dsi, ST7703_CMD_SETBGP, 0x08, 0x08); +- msleep(20); +- +- dsi_generic_write_seq(dsi, ST7703_CMD_SETVCOM, 0x3F, 0x3F); +- dsi_generic_write_seq(dsi, ST7703_CMD_UNKNOWN0, 0x02, 0x11, 0x00); +- dsi_generic_write_seq(dsi, ST7703_CMD_SETGIP1, +- 0x82, 0x10, 0x06, 0x05, 0x9E, 0x0A, 0xA5, 0x12, +- 0x31, 0x23, 0x37, 0x83, 0x04, 0xBC, 0x27, 0x38, +- 0x0C, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0C, 0x00, +- 0x03, 0x00, 0x00, 0x00, 0x75, 0x75, 0x31, 0x88, +- 0x88, 0x88, 0x88, 0x88, 0x88, 0x13, 0x88, 0x64, +- 0x64, 0x20, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, +- 0x02, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); +- dsi_generic_write_seq(dsi, ST7703_CMD_SETGIP2, +- 0x02, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +- 0x00, 0x00, 0x00, 0x00, 0x02, 0x46, 0x02, 0x88, +- 0x88, 0x88, 0x88, 0x88, 0x88, 0x64, 0x88, 0x13, +- 0x57, 0x13, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, +- 0x75, 0x88, 0x23, 0x14, 0x00, 0x00, 0x02, 0x00, +- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x0A, +- 0xA5, 0x00, 0x00, 0x00, 0x00); +- dsi_generic_write_seq(dsi, ST7703_CMD_SETGAMMA, +- 0x00, 0x09, 0x0E, 0x29, 0x2D, 0x3C, 0x41, 0x37, +- 0x07, 0x0B, 0x0D, 0x10, 0x11, 0x0F, 0x10, 0x11, +- 0x18, 0x00, 0x09, 0x0E, 0x29, 0x2D, 0x3C, 0x41, +- 0x37, 0x07, 0x0B, 0x0D, 0x10, 0x11, 0x0F, 0x10, +- 0x11, 0x18); +- msleep(20); +- +- ret = mipi_dsi_dcs_exit_sleep_mode(dsi); +- if (ret < 0) { +- DRM_DEV_ERROR(dev, "Failed to exit sleep mode: %d\n", ret); +- return ret; +- } +- /* Panel is operational 120 msec after reset */ +- msleep(60); +- ret = mipi_dsi_dcs_set_display_on(dsi); +- if (ret) +- return ret; +- +- DRM_DEV_DEBUG_DRIVER(dev, "Panel init sequence done\n"); +- return 0; +-} +- +-static int jh057n_enable(struct drm_panel *panel) +-{ +- struct jh057n *ctx = panel_to_jh057n(panel); +- int ret; +- +- ret = jh057n_init_sequence(ctx); +- if (ret < 0) { +- DRM_DEV_ERROR(ctx->dev, "Panel init sequence failed: %d\n", +- ret); +- return ret; +- } +- +- return 0; +-} +- +-static int jh057n_disable(struct drm_panel *panel) +-{ +- struct jh057n *ctx = panel_to_jh057n(panel); +- struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); +- +- return mipi_dsi_dcs_set_display_off(dsi); +-} +- +-static int jh057n_unprepare(struct drm_panel *panel) +-{ +- struct jh057n *ctx = panel_to_jh057n(panel); +- +- if (!ctx->prepared) +- return 0; +- +- regulator_disable(ctx->iovcc); +- regulator_disable(ctx->vcc); +- ctx->prepared = false; +- +- return 0; +-} +- +-static int jh057n_prepare(struct drm_panel *panel) +-{ +- struct jh057n *ctx = panel_to_jh057n(panel); +- int ret; +- +- if (ctx->prepared) +- return 0; +- +- DRM_DEV_DEBUG_DRIVER(ctx->dev, "Resetting the panel\n"); +- ret = regulator_enable(ctx->vcc); +- if (ret < 0) { +- DRM_DEV_ERROR(ctx->dev, +- "Failed to enable vcc supply: %d\n", ret); +- return ret; +- } +- ret = regulator_enable(ctx->iovcc); +- if (ret < 0) { +- DRM_DEV_ERROR(ctx->dev, +- "Failed to enable iovcc supply: %d\n", ret); +- goto disable_vcc; +- } +- +- gpiod_set_value_cansleep(ctx->reset_gpio, 1); +- usleep_range(20, 40); +- gpiod_set_value_cansleep(ctx->reset_gpio, 0); +- msleep(20); +- +- ctx->prepared = true; +- +- return 0; +- +-disable_vcc: +- regulator_disable(ctx->vcc); +- return ret; +-} +- +-static const struct drm_display_mode default_mode = { +- .hdisplay = 720, +- .hsync_start = 720 + 90, +- .hsync_end = 720 + 90 + 20, +- .htotal = 720 + 90 + 20 + 20, +- .vdisplay = 1440, +- .vsync_start = 1440 + 20, +- .vsync_end = 1440 + 20 + 4, +- .vtotal = 1440 + 20 + 4 + 12, +- .vrefresh = 60, +- .clock = 75276, +- .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, +- .width_mm = 65, +- .height_mm = 130, +-}; +- +-static int jh057n_get_modes(struct drm_panel *panel, +- struct drm_connector *connector) +-{ +- struct jh057n *ctx = panel_to_jh057n(panel); +- struct drm_display_mode *mode; +- +- mode = drm_mode_duplicate(connector->dev, &default_mode); +- if (!mode) { +- DRM_DEV_ERROR(ctx->dev, "Failed to add mode %ux%u@%u\n", +- default_mode.hdisplay, default_mode.vdisplay, +- default_mode.vrefresh); +- return -ENOMEM; +- } +- +- drm_mode_set_name(mode); +- +- mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; +- connector->display_info.width_mm = mode->width_mm; +- connector->display_info.height_mm = mode->height_mm; +- drm_mode_probed_add(connector, mode); +- +- return 1; +-} +- +-static const struct drm_panel_funcs jh057n_drm_funcs = { +- .disable = jh057n_disable, +- .unprepare = jh057n_unprepare, +- .prepare = jh057n_prepare, +- .enable = jh057n_enable, +- .get_modes = jh057n_get_modes, +-}; +- +-static int allpixelson_set(void *data, u64 val) +-{ +- struct jh057n *ctx = data; +- struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); +- +- DRM_DEV_DEBUG_DRIVER(ctx->dev, "Setting all pixels on\n"); +- dsi_generic_write_seq(dsi, ST7703_CMD_ALL_PIXEL_ON); +- msleep(val * 1000); +- /* Reset the panel to get video back */ +- drm_panel_disable(&ctx->panel); +- drm_panel_unprepare(&ctx->panel); +- drm_panel_prepare(&ctx->panel); +- drm_panel_enable(&ctx->panel); +- +- return 0; +-} +- +-DEFINE_SIMPLE_ATTRIBUTE(allpixelson_fops, NULL, +- allpixelson_set, "%llu\n"); +- +-static void jh057n_debugfs_init(struct jh057n *ctx) +-{ +- ctx->debugfs = debugfs_create_dir(DRV_NAME, NULL); +- +- debugfs_create_file("allpixelson", 0600, ctx->debugfs, ctx, +- &allpixelson_fops); +-} +- +-static void jh057n_debugfs_remove(struct jh057n *ctx) +-{ +- debugfs_remove_recursive(ctx->debugfs); +- ctx->debugfs = NULL; +-} +- +-static int jh057n_probe(struct mipi_dsi_device *dsi) +-{ +- struct device *dev = &dsi->dev; +- struct jh057n *ctx; +- int ret; +- +- ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); +- if (!ctx) +- return -ENOMEM; +- +- ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); +- if (IS_ERR(ctx->reset_gpio)) { +- DRM_DEV_ERROR(dev, "cannot get reset gpio\n"); +- return PTR_ERR(ctx->reset_gpio); +- } +- +- mipi_dsi_set_drvdata(dsi, ctx); +- +- ctx->dev = dev; +- +- dsi->lanes = 4; +- dsi->format = MIPI_DSI_FMT_RGB888; +- dsi->mode_flags = MIPI_DSI_MODE_VIDEO | +- MIPI_DSI_MODE_VIDEO_BURST | MIPI_DSI_MODE_VIDEO_SYNC_PULSE; +- +- ctx->vcc = devm_regulator_get(dev, "vcc"); +- if (IS_ERR(ctx->vcc)) { +- ret = PTR_ERR(ctx->vcc); +- if (ret != -EPROBE_DEFER) +- DRM_DEV_ERROR(dev, +- "Failed to request vcc regulator: %d\n", +- ret); +- return ret; +- } +- ctx->iovcc = devm_regulator_get(dev, "iovcc"); +- if (IS_ERR(ctx->iovcc)) { +- ret = PTR_ERR(ctx->iovcc); +- if (ret != -EPROBE_DEFER) +- DRM_DEV_ERROR(dev, +- "Failed to request iovcc regulator: %d\n", +- ret); +- return ret; +- } +- +- drm_panel_init(&ctx->panel, dev, &jh057n_drm_funcs, +- DRM_MODE_CONNECTOR_DSI); +- +- ret = drm_panel_of_backlight(&ctx->panel); +- if (ret) +- return ret; +- +- drm_panel_add(&ctx->panel); +- +- ret = mipi_dsi_attach(dsi); +- if (ret < 0) { +- DRM_DEV_ERROR(dev, +- "mipi_dsi_attach failed (%d). Is host ready?\n", +- ret); +- drm_panel_remove(&ctx->panel); +- return ret; +- } +- +- DRM_DEV_INFO(dev, "%ux%u@%u %ubpp dsi %udl - ready\n", +- default_mode.hdisplay, default_mode.vdisplay, +- default_mode.vrefresh, +- mipi_dsi_pixel_format_to_bpp(dsi->format), dsi->lanes); +- +- jh057n_debugfs_init(ctx); +- return 0; +-} +- +-static void jh057n_shutdown(struct mipi_dsi_device *dsi) +-{ +- struct jh057n *ctx = mipi_dsi_get_drvdata(dsi); +- int ret; +- +- ret = drm_panel_unprepare(&ctx->panel); +- if (ret < 0) +- DRM_DEV_ERROR(&dsi->dev, "Failed to unprepare panel: %d\n", +- ret); +- +- ret = drm_panel_disable(&ctx->panel); +- if (ret < 0) +- DRM_DEV_ERROR(&dsi->dev, "Failed to disable panel: %d\n", +- ret); +-} +- +-static int jh057n_remove(struct mipi_dsi_device *dsi) +-{ +- struct jh057n *ctx = mipi_dsi_get_drvdata(dsi); +- int ret; +- +- jh057n_shutdown(dsi); +- +- ret = mipi_dsi_detach(dsi); +- if (ret < 0) +- DRM_DEV_ERROR(&dsi->dev, "Failed to detach from DSI host: %d\n", +- ret); +- +- drm_panel_remove(&ctx->panel); +- +- jh057n_debugfs_remove(ctx); +- +- return 0; +-} +- +-static const struct of_device_id jh057n_of_match[] = { +- { .compatible = "rocktech,jh057n00900" }, +- { /* sentinel */ } +-}; +-MODULE_DEVICE_TABLE(of, jh057n_of_match); +- +-static struct mipi_dsi_driver jh057n_driver = { +- .probe = jh057n_probe, +- .remove = jh057n_remove, +- .shutdown = jh057n_shutdown, +- .driver = { +- .name = DRV_NAME, +- .of_match_table = jh057n_of_match, +- }, +-}; +-module_mipi_dsi_driver(jh057n_driver); +- +-MODULE_AUTHOR("Guido Günther <agx@sigxcpu.org>"); +-MODULE_DESCRIPTION("DRM driver for Rocktech JH057N00900 MIPI DSI panel"); +-MODULE_LICENSE("GPL v2"); +diff --git a/drivers/gpu/drm/panel/panel-sitronix-st7703.c b/drivers/gpu/drm/panel/panel-sitronix-st7703.c +new file mode 100644 +index 000000000000..38ff742bc120 +--- /dev/null ++++ b/drivers/gpu/drm/panel/panel-sitronix-st7703.c +@@ -0,0 +1,424 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Rockteck jh057n00900 5.5" MIPI-DSI panel driver ++ * ++ * Copyright (C) Purism SPC 2019 ++ */ ++ ++#include <linux/debugfs.h> ++#include <linux/delay.h> ++#include <linux/gpio/consumer.h> ++#include <linux/media-bus-format.h> ++#include <linux/mod_devicetable.h> ++#include <linux/module.h> ++#include <linux/regulator/consumer.h> ++ ++#include <video/display_timing.h> ++#include <video/mipi_display.h> ++ ++#include <drm/drm_mipi_dsi.h> ++#include <drm/drm_modes.h> ++#include <drm/drm_panel.h> ++#include <drm/drm_print.h> ++ ++#define DRV_NAME "panel-rocktech-jh057n00900" ++ ++/* Manufacturer specific Commands send via DSI */ ++#define ST7703_CMD_ALL_PIXEL_OFF 0x22 ++#define ST7703_CMD_ALL_PIXEL_ON 0x23 ++#define ST7703_CMD_SETDISP 0xB2 ++#define ST7703_CMD_SETRGBIF 0xB3 ++#define ST7703_CMD_SETCYC 0xB4 ++#define ST7703_CMD_SETBGP 0xB5 ++#define ST7703_CMD_SETVCOM 0xB6 ++#define ST7703_CMD_SETOTP 0xB7 ++#define ST7703_CMD_SETPOWER_EXT 0xB8 ++#define ST7703_CMD_SETEXTC 0xB9 ++#define ST7703_CMD_SETMIPI 0xBA ++#define ST7703_CMD_SETVDC 0xBC ++#define ST7703_CMD_UNKNOWN0 0xBF ++#define ST7703_CMD_SETSCR 0xC0 ++#define ST7703_CMD_SETPOWER 0xC1 ++#define ST7703_CMD_SETPANEL 0xCC ++#define ST7703_CMD_SETGAMMA 0xE0 ++#define ST7703_CMD_SETEQ 0xE3 ++#define ST7703_CMD_SETGIP1 0xE9 ++#define ST7703_CMD_SETGIP2 0xEA ++ ++struct jh057n { ++ struct device *dev; ++ struct drm_panel panel; ++ struct gpio_desc *reset_gpio; ++ struct regulator *vcc; ++ struct regulator *iovcc; ++ bool prepared; ++ ++ struct dentry *debugfs; ++}; ++ ++static inline struct jh057n *panel_to_jh057n(struct drm_panel *panel) ++{ ++ return container_of(panel, struct jh057n, panel); ++} ++ ++#define dsi_generic_write_seq(dsi, seq...) do { \ ++ static const u8 d[] = { seq }; \ ++ int ret; \ ++ ret = mipi_dsi_generic_write(dsi, d, ARRAY_SIZE(d)); \ ++ if (ret < 0) \ ++ return ret; \ ++ } while (0) ++ ++static int jh057n_init_sequence(struct jh057n *ctx) ++{ ++ struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); ++ struct device *dev = ctx->dev; ++ int ret; ++ ++ /* ++ * Init sequence was supplied by the panel vendor. Most of the commands ++ * resemble the ST7703 but the number of parameters often don't match ++ * so it's likely a clone. ++ */ ++ dsi_generic_write_seq(dsi, ST7703_CMD_SETEXTC, ++ 0xF1, 0x12, 0x83); ++ dsi_generic_write_seq(dsi, ST7703_CMD_SETRGBIF, ++ 0x10, 0x10, 0x05, 0x05, 0x03, 0xFF, 0x00, 0x00, ++ 0x00, 0x00); ++ dsi_generic_write_seq(dsi, ST7703_CMD_SETSCR, ++ 0x73, 0x73, 0x50, 0x50, 0x00, 0x00, 0x08, 0x70, ++ 0x00); ++ dsi_generic_write_seq(dsi, ST7703_CMD_SETVDC, 0x4E); ++ dsi_generic_write_seq(dsi, ST7703_CMD_SETPANEL, 0x0B); ++ dsi_generic_write_seq(dsi, ST7703_CMD_SETCYC, 0x80); ++ dsi_generic_write_seq(dsi, ST7703_CMD_SETDISP, 0xF0, 0x12, 0x30); ++ dsi_generic_write_seq(dsi, ST7703_CMD_SETEQ, ++ 0x07, 0x07, 0x0B, 0x0B, 0x03, 0x0B, 0x00, 0x00, ++ 0x00, 0x00, 0xFF, 0x00, 0xC0, 0x10); ++ dsi_generic_write_seq(dsi, ST7703_CMD_SETBGP, 0x08, 0x08); ++ msleep(20); ++ ++ dsi_generic_write_seq(dsi, ST7703_CMD_SETVCOM, 0x3F, 0x3F); ++ dsi_generic_write_seq(dsi, ST7703_CMD_UNKNOWN0, 0x02, 0x11, 0x00); ++ dsi_generic_write_seq(dsi, ST7703_CMD_SETGIP1, ++ 0x82, 0x10, 0x06, 0x05, 0x9E, 0x0A, 0xA5, 0x12, ++ 0x31, 0x23, 0x37, 0x83, 0x04, 0xBC, 0x27, 0x38, ++ 0x0C, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0C, 0x00, ++ 0x03, 0x00, 0x00, 0x00, 0x75, 0x75, 0x31, 0x88, ++ 0x88, 0x88, 0x88, 0x88, 0x88, 0x13, 0x88, 0x64, ++ 0x64, 0x20, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, ++ 0x02, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); ++ dsi_generic_write_seq(dsi, ST7703_CMD_SETGIP2, ++ 0x02, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x02, 0x46, 0x02, 0x88, ++ 0x88, 0x88, 0x88, 0x88, 0x88, 0x64, 0x88, 0x13, ++ 0x57, 0x13, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, ++ 0x75, 0x88, 0x23, 0x14, 0x00, 0x00, 0x02, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x0A, ++ 0xA5, 0x00, 0x00, 0x00, 0x00); ++ dsi_generic_write_seq(dsi, ST7703_CMD_SETGAMMA, ++ 0x00, 0x09, 0x0E, 0x29, 0x2D, 0x3C, 0x41, 0x37, ++ 0x07, 0x0B, 0x0D, 0x10, 0x11, 0x0F, 0x10, 0x11, ++ 0x18, 0x00, 0x09, 0x0E, 0x29, 0x2D, 0x3C, 0x41, ++ 0x37, 0x07, 0x0B, 0x0D, 0x10, 0x11, 0x0F, 0x10, ++ 0x11, 0x18); ++ msleep(20); ++ ++ ret = mipi_dsi_dcs_exit_sleep_mode(dsi); ++ if (ret < 0) { ++ DRM_DEV_ERROR(dev, "Failed to exit sleep mode: %d\n", ret); ++ return ret; ++ } ++ /* Panel is operational 120 msec after reset */ ++ msleep(60); ++ ret = mipi_dsi_dcs_set_display_on(dsi); ++ if (ret) ++ return ret; ++ ++ DRM_DEV_DEBUG_DRIVER(dev, "Panel init sequence done\n"); ++ return 0; ++} ++ ++static int jh057n_enable(struct drm_panel *panel) ++{ ++ struct jh057n *ctx = panel_to_jh057n(panel); ++ int ret; ++ ++ ret = jh057n_init_sequence(ctx); ++ if (ret < 0) { ++ DRM_DEV_ERROR(ctx->dev, "Panel init sequence failed: %d\n", ++ ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int jh057n_disable(struct drm_panel *panel) ++{ ++ struct jh057n *ctx = panel_to_jh057n(panel); ++ struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); ++ ++ return mipi_dsi_dcs_set_display_off(dsi); ++} ++ ++static int jh057n_unprepare(struct drm_panel *panel) ++{ ++ struct jh057n *ctx = panel_to_jh057n(panel); ++ ++ if (!ctx->prepared) ++ return 0; ++ ++ regulator_disable(ctx->iovcc); ++ regulator_disable(ctx->vcc); ++ ctx->prepared = false; ++ ++ return 0; ++} ++ ++static int jh057n_prepare(struct drm_panel *panel) ++{ ++ struct jh057n *ctx = panel_to_jh057n(panel); ++ int ret; ++ ++ if (ctx->prepared) ++ return 0; ++ ++ DRM_DEV_DEBUG_DRIVER(ctx->dev, "Resetting the panel\n"); ++ ret = regulator_enable(ctx->vcc); ++ if (ret < 0) { ++ DRM_DEV_ERROR(ctx->dev, ++ "Failed to enable vcc supply: %d\n", ret); ++ return ret; ++ } ++ ret = regulator_enable(ctx->iovcc); ++ if (ret < 0) { ++ DRM_DEV_ERROR(ctx->dev, ++ "Failed to enable iovcc supply: %d\n", ret); ++ goto disable_vcc; ++ } ++ ++ gpiod_set_value_cansleep(ctx->reset_gpio, 1); ++ usleep_range(20, 40); ++ gpiod_set_value_cansleep(ctx->reset_gpio, 0); ++ msleep(20); ++ ++ ctx->prepared = true; ++ ++ return 0; ++ ++disable_vcc: ++ regulator_disable(ctx->vcc); ++ return ret; ++} ++ ++static const struct drm_display_mode default_mode = { ++ .hdisplay = 720, ++ .hsync_start = 720 + 90, ++ .hsync_end = 720 + 90 + 20, ++ .htotal = 720 + 90 + 20 + 20, ++ .vdisplay = 1440, ++ .vsync_start = 1440 + 20, ++ .vsync_end = 1440 + 20 + 4, ++ .vtotal = 1440 + 20 + 4 + 12, ++ .vrefresh = 60, ++ .clock = 75276, ++ .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, ++ .width_mm = 65, ++ .height_mm = 130, ++}; ++ ++static int jh057n_get_modes(struct drm_panel *panel, ++ struct drm_connector *connector) ++{ ++ struct jh057n *ctx = panel_to_jh057n(panel); ++ struct drm_display_mode *mode; ++ ++ mode = drm_mode_duplicate(connector->dev, &default_mode); ++ if (!mode) { ++ DRM_DEV_ERROR(ctx->dev, "Failed to add mode %ux%u@%u\n", ++ default_mode.hdisplay, default_mode.vdisplay, ++ default_mode.vrefresh); ++ return -ENOMEM; ++ } ++ ++ drm_mode_set_name(mode); ++ ++ mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; ++ connector->display_info.width_mm = mode->width_mm; ++ connector->display_info.height_mm = mode->height_mm; ++ drm_mode_probed_add(connector, mode); ++ ++ return 1; ++} ++ ++static const struct drm_panel_funcs jh057n_drm_funcs = { ++ .disable = jh057n_disable, ++ .unprepare = jh057n_unprepare, ++ .prepare = jh057n_prepare, ++ .enable = jh057n_enable, ++ .get_modes = jh057n_get_modes, ++}; ++ ++static int allpixelson_set(void *data, u64 val) ++{ ++ struct jh057n *ctx = data; ++ struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); ++ ++ DRM_DEV_DEBUG_DRIVER(ctx->dev, "Setting all pixels on\n"); ++ dsi_generic_write_seq(dsi, ST7703_CMD_ALL_PIXEL_ON); ++ msleep(val * 1000); ++ /* Reset the panel to get video back */ ++ drm_panel_disable(&ctx->panel); ++ drm_panel_unprepare(&ctx->panel); ++ drm_panel_prepare(&ctx->panel); ++ drm_panel_enable(&ctx->panel); ++ ++ return 0; ++} ++ ++DEFINE_SIMPLE_ATTRIBUTE(allpixelson_fops, NULL, ++ allpixelson_set, "%llu\n"); ++ ++static void jh057n_debugfs_init(struct jh057n *ctx) ++{ ++ ctx->debugfs = debugfs_create_dir(DRV_NAME, NULL); ++ ++ debugfs_create_file("allpixelson", 0600, ctx->debugfs, ctx, ++ &allpixelson_fops); ++} ++ ++static void jh057n_debugfs_remove(struct jh057n *ctx) ++{ ++ debugfs_remove_recursive(ctx->debugfs); ++ ctx->debugfs = NULL; ++} ++ ++static int jh057n_probe(struct mipi_dsi_device *dsi) ++{ ++ struct device *dev = &dsi->dev; ++ struct jh057n *ctx; ++ int ret; ++ ++ ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); ++ if (!ctx) ++ return -ENOMEM; ++ ++ ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); ++ if (IS_ERR(ctx->reset_gpio)) { ++ DRM_DEV_ERROR(dev, "cannot get reset gpio\n"); ++ return PTR_ERR(ctx->reset_gpio); ++ } ++ ++ mipi_dsi_set_drvdata(dsi, ctx); ++ ++ ctx->dev = dev; ++ ++ dsi->lanes = 4; ++ dsi->format = MIPI_DSI_FMT_RGB888; ++ dsi->mode_flags = MIPI_DSI_MODE_VIDEO | ++ MIPI_DSI_MODE_VIDEO_BURST | MIPI_DSI_MODE_VIDEO_SYNC_PULSE; ++ ++ ctx->vcc = devm_regulator_get(dev, "vcc"); ++ if (IS_ERR(ctx->vcc)) { ++ ret = PTR_ERR(ctx->vcc); ++ if (ret != -EPROBE_DEFER) ++ DRM_DEV_ERROR(dev, ++ "Failed to request vcc regulator: %d\n", ++ ret); ++ return ret; ++ } ++ ctx->iovcc = devm_regulator_get(dev, "iovcc"); ++ if (IS_ERR(ctx->iovcc)) { ++ ret = PTR_ERR(ctx->iovcc); ++ if (ret != -EPROBE_DEFER) ++ DRM_DEV_ERROR(dev, ++ "Failed to request iovcc regulator: %d\n", ++ ret); ++ return ret; ++ } ++ ++ drm_panel_init(&ctx->panel, dev, &jh057n_drm_funcs, ++ DRM_MODE_CONNECTOR_DSI); ++ ++ ret = drm_panel_of_backlight(&ctx->panel); ++ if (ret) ++ return ret; ++ ++ drm_panel_add(&ctx->panel); ++ ++ ret = mipi_dsi_attach(dsi); ++ if (ret < 0) { ++ DRM_DEV_ERROR(dev, ++ "mipi_dsi_attach failed (%d). Is host ready?\n", ++ ret); ++ drm_panel_remove(&ctx->panel); ++ return ret; ++ } ++ ++ DRM_DEV_INFO(dev, "%ux%u@%u %ubpp dsi %udl - ready\n", ++ default_mode.hdisplay, default_mode.vdisplay, ++ default_mode.vrefresh, ++ mipi_dsi_pixel_format_to_bpp(dsi->format), dsi->lanes); ++ ++ jh057n_debugfs_init(ctx); ++ return 0; ++} ++ ++static void jh057n_shutdown(struct mipi_dsi_device *dsi) ++{ ++ struct jh057n *ctx = mipi_dsi_get_drvdata(dsi); ++ int ret; ++ ++ ret = drm_panel_unprepare(&ctx->panel); ++ if (ret < 0) ++ DRM_DEV_ERROR(&dsi->dev, "Failed to unprepare panel: %d\n", ++ ret); ++ ++ ret = drm_panel_disable(&ctx->panel); ++ if (ret < 0) ++ DRM_DEV_ERROR(&dsi->dev, "Failed to disable panel: %d\n", ++ ret); ++} ++ ++static int jh057n_remove(struct mipi_dsi_device *dsi) ++{ ++ struct jh057n *ctx = mipi_dsi_get_drvdata(dsi); ++ int ret; ++ ++ jh057n_shutdown(dsi); ++ ++ ret = mipi_dsi_detach(dsi); ++ if (ret < 0) ++ DRM_DEV_ERROR(&dsi->dev, "Failed to detach from DSI host: %d\n", ++ ret); ++ ++ drm_panel_remove(&ctx->panel); ++ ++ jh057n_debugfs_remove(ctx); ++ ++ return 0; ++} ++ ++static const struct of_device_id jh057n_of_match[] = { ++ { .compatible = "rocktech,jh057n00900" }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, jh057n_of_match); ++ ++static struct mipi_dsi_driver jh057n_driver = { ++ .probe = jh057n_probe, ++ .remove = jh057n_remove, ++ .shutdown = jh057n_shutdown, ++ .driver = { ++ .name = DRV_NAME, ++ .of_match_table = jh057n_of_match, ++ }, ++}; ++module_mipi_dsi_driver(jh057n_driver); ++ ++MODULE_AUTHOR("Guido Günther <agx@sigxcpu.org>"); ++MODULE_DESCRIPTION("DRM driver for Rocktech JH057N00900 MIPI DSI panel"); ++MODULE_LICENSE("GPL v2"); +-- +2.26.2 + |