summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/panel
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/panel')
-rw-r--r--drivers/gpu/drm/panel/Kconfig107
-rw-r--r--drivers/gpu/drm/panel/Makefile12
-rw-r--r--drivers/gpu/drm/panel/panel-arm-versatile.c11
-rw-r--r--drivers/gpu/drm/panel/panel-boe-himax8279d.c978
-rw-r--r--drivers/gpu/drm/panel/panel-feiyang-fy07024di26a30d.c21
-rw-r--r--drivers/gpu/drm/panel/panel-ilitek-ili9322.c58
-rw-r--r--drivers/gpu/drm/panel/panel-ilitek-ili9881c.c34
-rw-r--r--drivers/gpu/drm/panel/panel-innolux-p079zca.c48
-rw-r--r--drivers/gpu/drm/panel/panel-jdi-lt070me05000.c16
-rw-r--r--drivers/gpu/drm/panel/panel-kingdisplay-kd097d04.c46
-rw-r--r--drivers/gpu/drm/panel/panel-leadtek-ltk500hd1829.c531
-rw-r--r--drivers/gpu/drm/panel/panel-lg-lb035q02.c243
-rw-r--r--drivers/gpu/drm/panel/panel-lg-lg4573.c19
-rw-r--r--drivers/gpu/drm/panel/panel-lvds.c71
-rw-r--r--drivers/gpu/drm/panel/panel-nec-nl8048hl11.c254
-rw-r--r--drivers/gpu/drm/panel/panel-novatek-nt39016.c358
-rw-r--r--drivers/gpu/drm/panel/panel-olimex-lcd-olinuxino.c32
-rw-r--r--drivers/gpu/drm/panel/panel-orisetech-otm8009a.c16
-rw-r--r--drivers/gpu/drm/panel/panel-osd-osd101t2587-53ts.c40
-rw-r--r--drivers/gpu/drm/panel/panel-panasonic-vvx10f034n00.c67
-rw-r--r--drivers/gpu/drm/panel/panel-raspberrypi-touchscreen.c28
-rw-r--r--drivers/gpu/drm/panel/panel-raydium-rm67191.c667
-rw-r--r--drivers/gpu/drm/panel/panel-raydium-rm68200.c31
-rw-r--r--drivers/gpu/drm/panel/panel-rocktech-jh057n00900.c111
-rw-r--r--drivers/gpu/drm/panel/panel-ronbo-rb070d30.c36
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-ld9040.c9
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-s6d16d0.c11
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c11
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-s6e63j0x03.c11
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-s6e63m0.c11
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-s6e8aa0.c9
-rw-r--r--drivers/gpu/drm/panel/panel-seiko-43wvf1g.c57
-rw-r--r--drivers/gpu/drm/panel/panel-sharp-lq101r1sx01.c37
-rw-r--r--drivers/gpu/drm/panel/panel-sharp-ls037v7dw01.c225
-rw-r--r--drivers/gpu/drm/panel/panel-sharp-ls043t1le01.c40
-rw-r--r--drivers/gpu/drm/panel/panel-simple.c637
-rw-r--r--drivers/gpu/drm/panel/panel-sitronix-st7701.c28
-rw-r--r--drivers/gpu/drm/panel/panel-sitronix-st7789v.c53
-rw-r--r--drivers/gpu/drm/panel/panel-sony-acx424akp.c550
-rw-r--r--drivers/gpu/drm/panel/panel-sony-acx565akm.c707
-rw-r--r--drivers/gpu/drm/panel/panel-tpo-td028ttec1.c391
-rw-r--r--drivers/gpu/drm/panel/panel-tpo-td043mtea1.c515
-rw-r--r--drivers/gpu/drm/panel/panel-tpo-tpg110.c31
-rw-r--r--drivers/gpu/drm/panel/panel-truly-nt35597.c9
-rw-r--r--drivers/gpu/drm/panel/panel-xinpeng-xpp055c272.c398
45 files changed, 6893 insertions, 682 deletions
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index d9d931aa6e26..ae44ac2ec106 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -18,6 +18,17 @@ config DRM_PANEL_ARM_VERSATILE
reference designs. The panel is detected using special registers
in the Versatile family syscon registers.
+config DRM_PANEL_BOE_HIMAX8279D
+ tristate "Boe Himax8279d 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 Boe Himax8279d
+ TFT-LCD modules. The panel has a 1200x1920 resolution and uses
+ 24 bit RGB per pixel. It provides a MIPI DSI interface to
+ the host and has a built-in LED backlight.
+
config DRM_PANEL_LVDS
tristate "Generic LVDS panel driver"
depends on OF
@@ -98,11 +109,30 @@ config DRM_PANEL_KINGDISPLAY_KD097D04
24 bit RGB per pixel. It provides a MIPI DSI interface to
the host and has a built-in LED backlight.
+config DRM_PANEL_LEADTEK_LTK500HD1829
+ tristate "Leadtek LTK500HD1829 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 Kingdisplay kd097d04
+ TFT-LCD modules. The panel has a 1536x2048 resolution and uses
+ 24 bit RGB per pixel. It provides a MIPI DSI interface to
+ the host and has a built-in LED backlight.
+
config DRM_PANEL_SAMSUNG_LD9040
tristate "Samsung LD9040 RGB/SPI panel"
depends on OF && SPI
select VIDEOMODE_HELPERS
+config DRM_PANEL_LG_LB035Q02
+ tristate "LG LB035Q024573 RGB panel"
+ depends on GPIOLIB && OF && SPI
+ help
+ Say Y here if you want to enable support for the LB035Q02 RGB panel
+ (found on the Gumstix Overo Palo35 board). To compile this driver as
+ a module, choose M here.
+
config DRM_PANEL_LG_LG4573
tristate "LG4573 RGB/SPI panel"
depends on OF && SPI
@@ -111,6 +141,23 @@ config DRM_PANEL_LG_LG4573
Say Y here if you want to enable support for LG4573 RGB panel.
To compile this driver as a module, choose M here.
+config DRM_PANEL_NEC_NL8048HL11
+ tristate "NEC NL8048HL11 RGB panel"
+ depends on GPIOLIB && OF && SPI
+ help
+ Say Y here if you want to enable support for the NEC NL8048HL11 RGB
+ panel (found on the Zoom2/3/3630 SDP boards). To compile this driver
+ as a module, choose M here.
+
+config DRM_PANEL_NOVATEK_NT39016
+ tristate "Novatek NT39016 RGB/SPI panel"
+ depends on OF && SPI
+ depends on BACKLIGHT_CLASS_DEVICE
+ select REGMAP_SPI
+ help
+ Say Y here if you want to enable support for the panels built
+ around the Novatek NT39016 display controller.
+
config DRM_PANEL_OLIMEX_LCD_OLINUXINO
tristate "Olimex LCD-OLinuXino panel"
depends on OF
@@ -159,6 +206,15 @@ config DRM_PANEL_RASPBERRYPI_TOUCHSCREEN
Pi 7" Touchscreen. To compile this driver as a module,
choose M here.
+config DRM_PANEL_RAYDIUM_RM67191
+ tristate "Raydium RM67191 FHD 1080x1920 DSI video mode 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 Raydium RM67191 FHD
+ (1080x1920) DSI panel.
+
config DRM_PANEL_RAYDIUM_RM68200
tristate "Raydium RM68200 720x1280 DSI video mode panel"
depends on OF
@@ -248,6 +304,13 @@ config DRM_PANEL_SHARP_LQ101R1SX01
To compile this driver as a module, choose M here: the module
will be called panel-sharp-lq101r1sx01.
+config DRM_PANEL_SHARP_LS037V7DW01
+ tristate "Sharp LS037V7DW01 VGA LCD panel"
+ depends on GPIOLIB && OF && REGULATOR
+ help
+ Say Y here if you want to enable support for Sharp LS037V7DW01 VGA
+ (480x640) LCD panel (found on the TI SDP3430 board).
+
config DRM_PANEL_SHARP_LS043T1LE01
tristate "Sharp LS043T1LE01 qHD video mode panel"
depends on OF
@@ -275,6 +338,40 @@ config DRM_PANEL_SITRONIX_ST7789V
Say Y here if you want to enable support for the Sitronix
ST7789V controller for 240x320 LCD panels
+config DRM_PANEL_SONY_ACX424AKP
+ tristate "Sony ACX424AKP DSI command mode panel"
+ depends on OF
+ depends on DRM_MIPI_DSI
+ depends on BACKLIGHT_CLASS_DEVICE
+ select VIDEOMODE_HELPERS
+ help
+ Say Y here if you want to enable the Sony ACX424 display
+ panel. This panel supports DSI in both command and video
+ mode.
+
+config DRM_PANEL_SONY_ACX565AKM
+ tristate "Sony ACX565AKM panel"
+ depends on GPIOLIB && OF && SPI
+ depends on BACKLIGHT_CLASS_DEVICE
+ help
+ 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_TD028TTEC1
+ tristate "Toppoly (TPO) TD028TTEC1 panel driver"
+ depends on OF && SPI
+ depends on BACKLIGHT_CLASS_DEVICE
+ help
+ Say Y here if you want to enable support for TPO TD028TTEC1 480x640
+ 2.8" panel (found on the OpenMoko Neo FreeRunner and Neo 1973).
+
+config DRM_PANEL_TPO_TD043MTEA1
+ tristate "Toppoly (TPO) TD043MTEA1 panel driver"
+ depends on GPIOLIB && OF && REGULATOR && SPI
+ help
+ Say Y here if you want to enable support for TPO TD043MTEA1 800x480
+ 4.3" panel (found on the OMAP3 Pandora board).
+
config DRM_PANEL_TPO_TPG110
tristate "TPO TPG 800x400 panel"
depends on OF && SPI && GPIOLIB
@@ -291,4 +388,14 @@ config DRM_PANEL_TRULY_NT35597_WQXGA
help
Say Y here if you want to enable support for Truly NT35597 WQXGA Dual DSI
Video Mode panel
+
+config DRM_PANEL_XINPENG_XPP055C272
+ tristate "Xinpeng XPP055C272 panel driver"
+ depends on OF
+ depends on DRM_MIPI_DSI
+ depends on BACKLIGHT_CLASS_DEVICE
+ help
+ Say Y here if you want to enable support for the Xinpeng
+ XPP055C272 controller for 720x1280 LCD panels with MIPI/RGB/SPI
+ system interfaces.
endmenu
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index fb0cb3aaa9e6..7c4d3c581fd4 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -1,5 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_DRM_PANEL_ARM_VERSATILE) += panel-arm-versatile.o
+obj-$(CONFIG_DRM_PANEL_BOE_HIMAX8279D) += panel-boe-himax8279d.o
obj-$(CONFIG_DRM_PANEL_LVDS) += panel-lvds.o
obj-$(CONFIG_DRM_PANEL_SIMPLE) += panel-simple.o
obj-$(CONFIG_DRM_PANEL_FEIYANG_FY07024DI26A30D) += panel-feiyang-fy07024di26a30d.o
@@ -8,12 +9,17 @@ obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9881C) += panel-ilitek-ili9881c.o
obj-$(CONFIG_DRM_PANEL_INNOLUX_P079ZCA) += panel-innolux-p079zca.o
obj-$(CONFIG_DRM_PANEL_JDI_LT070ME05000) += panel-jdi-lt070me05000.o
obj-$(CONFIG_DRM_PANEL_KINGDISPLAY_KD097D04) += panel-kingdisplay-kd097d04.o
+obj-$(CONFIG_DRM_PANEL_LEADTEK_LTK500HD1829) += panel-leadtek-ltk500hd1829.o
+obj-$(CONFIG_DRM_PANEL_LG_LB035Q02) += panel-lg-lb035q02.o
obj-$(CONFIG_DRM_PANEL_LG_LG4573) += panel-lg-lg4573.o
+obj-$(CONFIG_DRM_PANEL_NEC_NL8048HL11) += panel-nec-nl8048hl11.o
+obj-$(CONFIG_DRM_PANEL_NOVATEK_NT39016) += panel-novatek-nt39016.o
obj-$(CONFIG_DRM_PANEL_OLIMEX_LCD_OLINUXINO) += panel-olimex-lcd-olinuxino.o
obj-$(CONFIG_DRM_PANEL_ORISETECH_OTM8009A) += panel-orisetech-otm8009a.o
obj-$(CONFIG_DRM_PANEL_OSD_OSD101T2587_53TS) += panel-osd-osd101t2587-53ts.o
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
@@ -25,8 +31,14 @@ obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E63M0) += panel-samsung-s6e63m0.o
obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o
obj-$(CONFIG_DRM_PANEL_SEIKO_43WVF1G) += panel-seiko-43wvf1g.o
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_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
+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
obj-$(CONFIG_DRM_PANEL_TRULY_NT35597_WQXGA) += panel-truly-nt35597.o
+obj-$(CONFIG_DRM_PANEL_XINPENG_XPP055C272) += panel-xinpeng-xpp055c272.o
diff --git a/drivers/gpu/drm/panel/panel-arm-versatile.c b/drivers/gpu/drm/panel/panel-arm-versatile.c
index 5f72c922a04b..41444a73c980 100644
--- a/drivers/gpu/drm/panel/panel-arm-versatile.c
+++ b/drivers/gpu/drm/panel/panel-arm-versatile.c
@@ -260,9 +260,9 @@ static int versatile_panel_enable(struct drm_panel *panel)
return 0;
}
-static int versatile_panel_get_modes(struct drm_panel *panel)
+static int versatile_panel_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
{
- struct drm_connector *connector = panel->connector;
struct versatile_panel *vpanel = to_versatile_panel(panel);
struct drm_display_mode *mode;
@@ -270,7 +270,7 @@ static int versatile_panel_get_modes(struct drm_panel *panel)
connector->display_info.height_mm = vpanel->panel_type->height_mm;
connector->display_info.bus_flags = vpanel->panel_type->bus_flags;
- mode = drm_mode_duplicate(panel->drm, &vpanel->panel_type->mode);
+ mode = drm_mode_duplicate(connector->dev, &vpanel->panel_type->mode);
drm_mode_set_name(mode);
mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
@@ -350,9 +350,8 @@ static int versatile_panel_probe(struct platform_device *pdev)
dev_info(dev, "panel mounted on IB2 daughterboard\n");
}
- drm_panel_init(&vpanel->panel);
- vpanel->panel.dev = dev;
- vpanel->panel.funcs = &versatile_panel_drm_funcs;
+ drm_panel_init(&vpanel->panel, dev, &versatile_panel_drm_funcs,
+ DRM_MODE_CONNECTOR_DPI);
return drm_panel_add(&vpanel->panel);
}
diff --git a/drivers/gpu/drm/panel/panel-boe-himax8279d.c b/drivers/gpu/drm/panel/panel-boe-himax8279d.c
new file mode 100644
index 000000000000..74d58ee7d04c
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-boe-himax8279d.c
@@ -0,0 +1,978 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019, Huaqin Telecom Technology Co., Ltd
+ *
+ * Author: Jerry Han <jerry.han.hq@gmail.com>
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
+
+#include <drm/drm_device.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_print.h>
+
+#include <video/mipi_display.h>
+
+struct panel_cmd {
+ char cmd;
+ char data;
+};
+
+struct panel_desc {
+ const struct drm_display_mode *display_mode;
+ unsigned int bpc;
+ unsigned int width_mm;
+ unsigned int height_mm;
+
+ unsigned long mode_flags;
+ enum mipi_dsi_pixel_format format;
+ unsigned int lanes;
+ const struct panel_cmd *on_cmds;
+ unsigned int on_cmds_num;
+};
+
+struct panel_info {
+ struct drm_panel base;
+ struct mipi_dsi_device *link;
+ const struct panel_desc *desc;
+
+ struct gpio_desc *enable_gpio;
+ struct gpio_desc *pp33_gpio;
+ struct gpio_desc *pp18_gpio;
+
+ bool prepared;
+ bool enabled;
+};
+
+static inline struct panel_info *to_panel_info(struct drm_panel *panel)
+{
+ return container_of(panel, struct panel_info, base);
+}
+
+static void disable_gpios(struct panel_info *pinfo)
+{
+ gpiod_set_value(pinfo->enable_gpio, 0);
+ gpiod_set_value(pinfo->pp33_gpio, 0);
+ gpiod_set_value(pinfo->pp18_gpio, 0);
+}
+
+static int send_mipi_cmds(struct drm_panel *panel, const struct panel_cmd *cmds)
+{
+ struct panel_info *pinfo = to_panel_info(panel);
+ unsigned int i = 0;
+ int err;
+
+ for (i = 0; i < pinfo->desc->on_cmds_num; i++) {
+ err = mipi_dsi_dcs_write_buffer(pinfo->link, &cmds[i],
+ sizeof(struct panel_cmd));
+
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static int boe_panel_disable(struct drm_panel *panel)
+{
+ struct panel_info *pinfo = to_panel_info(panel);
+ int err;
+
+ if (!pinfo->enabled)
+ return 0;
+
+ err = mipi_dsi_dcs_set_display_off(pinfo->link);
+ if (err < 0) {
+ DRM_DEV_ERROR(panel->dev, "failed to set display off: %d\n",
+ err);
+ return err;
+ }
+
+ pinfo->enabled = false;
+
+ return 0;
+}
+
+static int boe_panel_unprepare(struct drm_panel *panel)
+{
+ struct panel_info *pinfo = to_panel_info(panel);
+ int err;
+
+ if (!pinfo->prepared)
+ return 0;
+
+ err = mipi_dsi_dcs_set_display_off(pinfo->link);
+ if (err < 0)
+ DRM_DEV_ERROR(panel->dev, "failed to set display off: %d\n",
+ err);
+
+ err = mipi_dsi_dcs_enter_sleep_mode(pinfo->link);
+ if (err < 0)
+ DRM_DEV_ERROR(panel->dev, "failed to enter sleep mode: %d\n",
+ err);
+
+ /* sleep_mode_delay: 1ms - 2ms */
+ usleep_range(1000, 2000);
+
+ disable_gpios(pinfo);
+
+ pinfo->prepared = false;
+
+ return 0;
+}
+
+static int boe_panel_prepare(struct drm_panel *panel)
+{
+ struct panel_info *pinfo = to_panel_info(panel);
+ int err;
+
+ if (pinfo->prepared)
+ return 0;
+
+ gpiod_set_value(pinfo->pp18_gpio, 1);
+ /* T1: 5ms - 6ms */
+ usleep_range(5000, 6000);
+ gpiod_set_value(pinfo->pp33_gpio, 1);
+
+ /* reset sequence */
+ /* T2: 14ms - 15ms */
+ usleep_range(14000, 15000);
+ gpiod_set_value(pinfo->enable_gpio, 1);
+
+ /* T3: 1ms - 2ms */
+ usleep_range(1000, 2000);
+ gpiod_set_value(pinfo->enable_gpio, 0);
+
+ /* T4: 1ms - 2ms */
+ usleep_range(1000, 2000);
+ gpiod_set_value(pinfo->enable_gpio, 1);
+
+ /* T5: 5ms - 6ms */
+ usleep_range(5000, 6000);
+
+ /* send init code */
+ err = send_mipi_cmds(panel, pinfo->desc->on_cmds);
+ if (err < 0) {
+ DRM_DEV_ERROR(panel->dev, "failed to send DCS Init Code: %d\n",
+ err);
+ goto poweroff;
+ }
+
+ err = mipi_dsi_dcs_exit_sleep_mode(pinfo->link);
+ if (err < 0) {
+ DRM_DEV_ERROR(panel->dev, "failed to exit sleep mode: %d\n",
+ err);
+ goto poweroff;
+ }
+
+ /* T6: 120ms - 121ms */
+ usleep_range(120000, 121000);
+
+ err = mipi_dsi_dcs_set_display_on(pinfo->link);
+ if (err < 0) {
+ DRM_DEV_ERROR(panel->dev, "failed to set display on: %d\n",
+ err);
+ goto poweroff;
+ }
+
+ /* T7: 20ms - 21ms */
+ usleep_range(20000, 21000);
+
+ pinfo->prepared = true;
+
+ return 0;
+
+poweroff:
+ disable_gpios(pinfo);
+ return err;
+}
+
+static int boe_panel_enable(struct drm_panel *panel)
+{
+ struct panel_info *pinfo = to_panel_info(panel);
+ int ret;
+
+ if (pinfo->enabled)
+ return 0;
+
+ usleep_range(120000, 121000);
+
+ ret = mipi_dsi_dcs_set_display_on(pinfo->link);
+ if (ret < 0) {
+ DRM_DEV_ERROR(panel->dev, "failed to set display on: %d\n",
+ ret);
+ return ret;
+ }
+
+ pinfo->enabled = true;
+
+ return 0;
+}
+
+static int boe_panel_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
+{
+ struct panel_info *pinfo = to_panel_info(panel);
+ const struct drm_display_mode *m = pinfo->desc->display_mode;
+ struct drm_display_mode *mode;
+
+ mode = drm_mode_duplicate(connector->dev, m);
+ if (!mode) {
+ DRM_DEV_ERROR(pinfo->base.dev, "failed to add mode %ux%u@%u\n",
+ m->hdisplay, m->vdisplay, m->vrefresh);
+ return -ENOMEM;
+ }
+
+ drm_mode_set_name(mode);
+
+ drm_mode_probed_add(connector, mode);
+
+ connector->display_info.width_mm = pinfo->desc->width_mm;
+ connector->display_info.height_mm = pinfo->desc->height_mm;
+ connector->display_info.bpc = pinfo->desc->bpc;
+
+ return 1;
+}
+
+static const struct drm_panel_funcs panel_funcs = {
+ .disable = boe_panel_disable,
+ .unprepare = boe_panel_unprepare,
+ .prepare = boe_panel_prepare,
+ .enable = boe_panel_enable,
+ .get_modes = boe_panel_get_modes,
+};
+
+static const struct drm_display_mode default_display_mode = {
+ .clock = 159420,
+ .hdisplay = 1200,
+ .hsync_start = 1200 + 80,
+ .hsync_end = 1200 + 80 + 60,
+ .htotal = 1200 + 80 + 60 + 24,
+ .vdisplay = 1920,
+ .vsync_start = 1920 + 10,
+ .vsync_end = 1920 + 10 + 14,
+ .vtotal = 1920 + 10 + 14 + 4,
+ .vrefresh = 60,
+};
+
+/* 8 inch */
+static const struct panel_cmd boe_himax8279d8p_on_cmds[] = {
+ { 0xB0, 0x05 },
+ { 0xB1, 0xE5 },
+ { 0xB3, 0x52 },
+ { 0xC0, 0x00 },
+ { 0xC2, 0x57 },
+ { 0xD9, 0x85 },
+ { 0xB0, 0x01 },
+ { 0xC8, 0x00 },
+ { 0xC9, 0x00 },
+ { 0xCC, 0x26 },
+ { 0xCD, 0x26 },
+ { 0xDC, 0x00 },
+ { 0xDD, 0x00 },
+ { 0xE0, 0x26 },
+ { 0xE1, 0x26 },
+ { 0xB0, 0x03 },
+ { 0xC3, 0x2A },
+ { 0xE7, 0x2A },
+ { 0xC5, 0x2A },
+ { 0xDE, 0x2A },
+ { 0xBC, 0x02 },
+ { 0xCB, 0x02 },
+ { 0xB0, 0x00 },
+ { 0xB6, 0x03 },
+ { 0xBA, 0x8B },
+ { 0xBF, 0x15 },
+ { 0xC0, 0x18 },
+ { 0xC2, 0x14 },
+ { 0xC3, 0x02 },
+ { 0xC4, 0x14 },
+ { 0xC5, 0x02 },
+ { 0xCC, 0x0A },
+ { 0xB0, 0x06 },
+ { 0xC0, 0xA5 },
+ { 0xD5, 0x20 },
+ { 0xC0, 0x00 },
+ { 0xB0, 0x02 },
+ { 0xC0, 0x00 },
+ { 0xC1, 0x02 },
+ { 0xC2, 0x06 },
+ { 0xC3, 0x16 },
+ { 0xC4, 0x0E },
+ { 0xC5, 0x18 },
+ { 0xC6, 0x26 },
+ { 0xC7, 0x32 },
+ { 0xC8, 0x3F },
+ { 0xC9, 0x3F },
+ { 0xCA, 0x3F },
+ { 0xCB, 0x3F },
+ { 0xCC, 0x3D },
+ { 0xCD, 0x2F },
+ { 0xCE, 0x2F },
+ { 0xCF, 0x2F },
+ { 0xD0, 0x07 },
+ { 0xD2, 0x00 },
+ { 0xD3, 0x02 },
+ { 0xD4, 0x06 },
+ { 0xD5, 0x12 },
+ { 0xD6, 0x0A },
+ { 0xD7, 0x14 },
+ { 0xD8, 0x22 },
+ { 0xD9, 0x2E },
+ { 0xDA, 0x3D },
+ { 0xDB, 0x3F },
+ { 0xDC, 0x3F },
+ { 0xDD, 0x3F },
+ { 0xDE, 0x3D },
+ { 0xDF, 0x2F },
+ { 0xE0, 0x2F },
+ { 0xE1, 0x2F },
+ { 0xE2, 0x07 },
+ { 0xB0, 0x07 },
+ { 0xB1, 0x18 },
+ { 0xB2, 0x19 },
+ { 0xB3, 0x2E },
+ { 0xB4, 0x52 },
+ { 0xB5, 0x72 },
+ { 0xB6, 0x8C },
+ { 0xB7, 0xBD },
+ { 0xB8, 0xEB },
+ { 0xB9, 0x47 },
+ { 0xBA, 0x96 },
+ { 0xBB, 0x1E },
+ { 0xBC, 0x90 },
+ { 0xBD, 0x93 },
+ { 0xBE, 0xFA },
+ { 0xBF, 0x56 },
+ { 0xC0, 0x8C },
+ { 0xC1, 0xB7 },
+ { 0xC2, 0xCC },
+ { 0xC3, 0xDF },
+ { 0xC4, 0xE8 },
+ { 0xC5, 0xF0 },
+ { 0xC6, 0xF8 },
+ { 0xC7, 0xFA },
+ { 0xC8, 0xFC },
+ { 0xC9, 0x00 },
+ { 0xCA, 0x00 },
+ { 0xCB, 0x5A },
+ { 0xCC, 0xAF },
+ { 0xCD, 0xFF },
+ { 0xCE, 0xFF },
+ { 0xB0, 0x08 },
+ { 0xB1, 0x04 },
+ { 0xB2, 0x15 },
+ { 0xB3, 0x2D },
+ { 0xB4, 0x51 },
+ { 0xB5, 0x72 },
+ { 0xB6, 0x8D },
+ { 0xB7, 0xBE },
+ { 0xB8, 0xED },
+ { 0xB9, 0x4A },
+ { 0xBA, 0x9A },
+ { 0xBB, 0x23 },
+ { 0xBC, 0x95 },
+ { 0xBD, 0x98 },
+ { 0xBE, 0xFF },
+ { 0xBF, 0x59 },
+ { 0xC0, 0x8E },
+ { 0xC1, 0xB9 },
+ { 0xC2, 0xCD },
+ { 0xC3, 0xDF },
+ { 0xC4, 0xE8 },
+ { 0xC5, 0xF0 },
+ { 0xC6, 0xF8 },
+ { 0xC7, 0xFA },
+ { 0xC8, 0xFC },
+ { 0xC9, 0x00 },
+ { 0xCA, 0x00 },
+ { 0xCB, 0x5A },
+ { 0xCC, 0xAF },
+ { 0xCD, 0xFF },
+ { 0xCE, 0xFF },
+ { 0xB0, 0x09 },
+ { 0xB1, 0x04 },
+ { 0xB2, 0x2C },
+ { 0xB3, 0x36 },
+ { 0xB4, 0x53 },
+ { 0xB5, 0x73 },
+ { 0xB6, 0x8E },
+ { 0xB7, 0xC0 },
+ { 0xB8, 0xEF },
+ { 0xB9, 0x4C },
+ { 0xBA, 0x9D },
+ { 0xBB, 0x25 },
+ { 0xBC, 0x96 },
+ { 0xBD, 0x9A },
+ { 0xBE, 0x01 },
+ { 0xBF, 0x59 },
+ { 0xC0, 0x8E },
+ { 0xC1, 0xB9 },
+ { 0xC2, 0xCD },
+ { 0xC3, 0xDF },
+ { 0xC4, 0xE8 },
+ { 0xC5, 0xF0 },
+ { 0xC6, 0xF8 },
+ { 0xC7, 0xFA },
+ { 0xC8, 0xFC },
+ { 0xC9, 0x00 },
+ { 0xCA, 0x00 },
+ { 0xCB, 0x5A },
+ { 0xCC, 0xBF },
+ { 0xCD, 0xFF },
+ { 0xCE, 0xFF },
+ { 0xB0, 0x0A },
+ { 0xB1, 0x18 },
+ { 0xB2, 0x19 },
+ { 0xB3, 0x2E },
+ { 0xB4, 0x52 },
+ { 0xB5, 0x72 },
+ { 0xB6, 0x8C },
+ { 0xB7, 0xBD },
+ { 0xB8, 0xEB },
+ { 0xB9, 0x47 },
+ { 0xBA, 0x96 },
+ { 0xBB, 0x1E },
+ { 0xBC, 0x90 },
+ { 0xBD, 0x93 },
+ { 0xBE, 0xFA },
+ { 0xBF, 0x56 },
+ { 0xC0, 0x8C },
+ { 0xC1, 0xB7 },
+ { 0xC2, 0xCC },
+ { 0xC3, 0xDF },
+ { 0xC4, 0xE8 },
+ { 0xC5, 0xF0 },
+ { 0xC6, 0xF8 },
+ { 0xC7, 0xFA },
+ { 0xC8, 0xFC },
+ { 0xC9, 0x00 },
+ { 0xCA, 0x00 },
+ { 0xCB, 0x5A },
+ { 0xCC, 0xAF },
+ { 0xCD, 0xFF },
+ { 0xCE, 0xFF },
+ { 0xB0, 0x0B },
+ { 0xB1, 0x04 },
+ { 0xB2, 0x15 },
+ { 0xB3, 0x2D },
+ { 0xB4, 0x51 },
+ { 0xB5, 0x72 },
+ { 0xB6, 0x8D },
+ { 0xB7, 0xBE },
+ { 0xB8, 0xED },
+ { 0xB9, 0x4A },
+ { 0xBA, 0x9A },
+ { 0xBB, 0x23 },
+ { 0xBC, 0x95 },
+ { 0xBD, 0x98 },
+ { 0xBE, 0xFF },
+ { 0xBF, 0x59 },
+ { 0xC0, 0x8E },
+ { 0xC1, 0xB9 },
+ { 0xC2, 0xCD },
+ { 0xC3, 0xDF },
+ { 0xC4, 0xE8 },
+ { 0xC5, 0xF0 },
+ { 0xC6, 0xF8 },
+ { 0xC7, 0xFA },
+ { 0xC8, 0xFC },
+ { 0xC9, 0x00 },
+ { 0xCA, 0x00 },
+ { 0xCB, 0x5A },
+ { 0xCC, 0xAF },
+ { 0xCD, 0xFF },
+ { 0xCE, 0xFF },
+ { 0xB0, 0x0C },
+ { 0xB1, 0x04 },
+ { 0xB2, 0x2C },
+ { 0xB3, 0x36 },
+ { 0xB4, 0x53 },
+ { 0xB5, 0x73 },
+ { 0xB6, 0x8E },
+ { 0xB7, 0xC0 },
+ { 0xB8, 0xEF },
+ { 0xB9, 0x4C },
+ { 0xBA, 0x9D },
+ { 0xBB, 0x25 },
+ { 0xBC, 0x96 },
+ { 0xBD, 0x9A },
+ { 0xBE, 0x01 },
+ { 0xBF, 0x59 },
+ { 0xC0, 0x8E },
+ { 0xC1, 0xB9 },
+ { 0xC2, 0xCD },
+ { 0xC3, 0xDF },
+ { 0xC4, 0xE8 },
+ { 0xC5, 0xF0 },
+ { 0xC6, 0xF8 },
+ { 0xC7, 0xFA },
+ { 0xC8, 0xFC },
+ { 0xC9, 0x00 },
+ { 0xCA, 0x00 },
+ { 0xCB, 0x5A },
+ { 0xCC, 0xBF },
+ { 0xCD, 0xFF },
+ { 0xCE, 0xFF },
+ { 0xB0, 0x04 },
+ { 0xB5, 0x02 },
+ { 0xB6, 0x01 },
+};
+
+static const struct panel_desc boe_himax8279d8p_panel_desc = {
+ .display_mode = &default_display_mode,
+ .bpc = 8,
+ .width_mm = 107,
+ .height_mm = 172,
+ .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
+ MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM,
+ .format = MIPI_DSI_FMT_RGB888,
+ .lanes = 4,
+ .on_cmds = boe_himax8279d8p_on_cmds,
+ .on_cmds_num = 260,
+};
+
+/* 10 inch */
+static const struct panel_cmd boe_himax8279d10p_on_cmds[] = {
+ { 0xB0, 0x05 },
+ { 0xB1, 0xE5 },
+ { 0xB3, 0x52 },
+ { 0xB0, 0x00 },
+ { 0xB6, 0x03 },
+ { 0xBA, 0x8B },
+ { 0xBF, 0x1A },
+ { 0xC0, 0x0F },
+ { 0xC2, 0x0C },
+ { 0xC3, 0x02 },
+ { 0xC4, 0x0C },
+ { 0xC5, 0x02 },
+ { 0xB0, 0x01 },
+ { 0xE0, 0x26 },
+ { 0xE1, 0x26 },
+ { 0xDC, 0x00 },
+ { 0xDD, 0x00 },
+ { 0xCC, 0x26 },
+ { 0xCD, 0x26 },
+ { 0xC8, 0x00 },
+ { 0xC9, 0x00 },
+ { 0xD2, 0x03 },
+ { 0xD3, 0x03 },
+ { 0xE6, 0x04 },
+ { 0xE7, 0x04 },
+ { 0xC4, 0x09 },
+ { 0xC5, 0x09 },
+ { 0xD8, 0x0A },
+ { 0xD9, 0x0A },
+ { 0xC2, 0x0B },
+ { 0xC3, 0x0B },
+ { 0xD6, 0x0C },
+ { 0xD7, 0x0C },
+ { 0xC0, 0x05 },
+ { 0xC1, 0x05 },
+ { 0xD4, 0x06 },
+ { 0xD5, 0x06 },
+ { 0xCA, 0x07 },
+ { 0xCB, 0x07 },
+ { 0xDE, 0x08 },
+ { 0xDF, 0x08 },
+ { 0xB0, 0x02 },
+ { 0xC0, 0x00 },
+ { 0xC1, 0x0D },
+ { 0xC2, 0x17 },
+ { 0xC3, 0x26 },
+ { 0xC4, 0x31 },
+ { 0xC5, 0x1C },
+ { 0xC6, 0x2C },
+ { 0xC7, 0x33 },
+ { 0xC8, 0x31 },
+ { 0xC9, 0x37 },
+ { 0xCA, 0x37 },
+ { 0xCB, 0x37 },
+ { 0xCC, 0x39 },
+ { 0xCD, 0x2E },
+ { 0xCE, 0x2F },
+ { 0xCF, 0x2F },
+ { 0xD0, 0x07 },
+ { 0xD2, 0x00 },
+ { 0xD3, 0x0D },
+ { 0xD4, 0x17 },
+ { 0xD5, 0x26 },
+ { 0xD6, 0x31 },
+ { 0xD7, 0x3F },
+ { 0xD8, 0x3F },
+ { 0xD9, 0x3F },
+ { 0xDA, 0x3F },
+ { 0xDB, 0x37 },
+ { 0xDC, 0x37 },
+ { 0xDD, 0x37 },
+ { 0xDE, 0x39 },
+ { 0xDF, 0x2E },
+ { 0xE0, 0x2F },
+ { 0xE1, 0x2F },
+ { 0xE2, 0x07 },
+ { 0xB0, 0x03 },
+ { 0xC8, 0x0B },
+ { 0xC9, 0x07 },
+ { 0xC3, 0x00 },
+ { 0xE7, 0x00 },
+ { 0xC5, 0x2A },
+ { 0xDE, 0x2A },
+ { 0xCA, 0x43 },
+ { 0xC9, 0x07 },
+ { 0xE4, 0xC0 },
+ { 0xE5, 0x0D },
+ { 0xCB, 0x01 },
+ { 0xBC, 0x01 },
+ { 0xB0, 0x06 },
+ { 0xB8, 0xA5 },
+ { 0xC0, 0xA5 },
+ { 0xC7, 0x0F },
+ { 0xD5, 0x32 },
+ { 0xB8, 0x00 },
+ { 0xC0, 0x00 },
+ { 0xBC, 0x00 },
+ { 0xB0, 0x07 },
+ { 0xB1, 0x00 },
+ { 0xB2, 0x05 },
+ { 0xB3, 0x10 },
+ { 0xB4, 0x22 },
+ { 0xB5, 0x36 },
+ { 0xB6, 0x4A },
+ { 0xB7, 0x6C },
+ { 0xB8, 0x9A },
+ { 0xB9, 0xD7 },
+ { 0xBA, 0x17 },
+ { 0xBB, 0x92 },
+ { 0xBC, 0x15 },
+ { 0xBD, 0x18 },
+ { 0xBE, 0x8C },
+ { 0xBF, 0x00 },
+ { 0xC0, 0x3A },
+ { 0xC1, 0x72 },
+ { 0xC2, 0x8C },
+ { 0xC3, 0xA5 },
+ { 0xC4, 0xB1 },
+ { 0xC5, 0xBE },
+ { 0xC6, 0xCA },
+ { 0xC7, 0xD1 },
+ { 0xC8, 0xD4 },
+ { 0xC9, 0x00 },
+ { 0xCA, 0x00 },
+ { 0xCB, 0x16 },
+ { 0xCC, 0xAF },
+ { 0xCD, 0xFF },
+ { 0xCE, 0xFF },
+ { 0xB0, 0x08 },
+ { 0xB1, 0x04 },
+ { 0xB2, 0x05 },
+ { 0xB3, 0x11 },
+ { 0xB4, 0x24 },
+ { 0xB5, 0x39 },
+ { 0xB6, 0x4E },
+ { 0xB7, 0x72 },
+ { 0xB8, 0xA3 },
+ { 0xB9, 0xE1 },
+ { 0xBA, 0x25 },
+ { 0xBB, 0xA8 },
+ { 0xBC, 0x2E },
+ { 0xBD, 0x32 },
+ { 0xBE, 0xAD },
+ { 0xBF, 0x28 },
+ { 0xC0, 0x63 },
+ { 0xC1, 0x9B },
+ { 0xC2, 0xB5 },
+ { 0xC3, 0xCF },
+ { 0xC4, 0xDB },
+ { 0xC5, 0xE8 },
+ { 0xC6, 0xF5 },
+ { 0xC7, 0xFA },
+ { 0xC8, 0xFC },
+ { 0xC9, 0x00 },
+ { 0xCA, 0x00 },
+ { 0xCB, 0x16 },
+ { 0xCC, 0xAF },
+ { 0xCD, 0xFF },
+ { 0xCE, 0xFF },
+ { 0xB0, 0x09 },
+ { 0xB1, 0x04 },
+ { 0xB2, 0x04 },
+ { 0xB3, 0x0F },
+ { 0xB4, 0x22 },
+ { 0xB5, 0x37 },
+ { 0xB6, 0x4D },
+ { 0xB7, 0x71 },
+ { 0xB8, 0xA2 },
+ { 0xB9, 0xE1 },
+ { 0xBA, 0x26 },
+ { 0xBB, 0xA9 },
+ { 0xBC, 0x2F },
+ { 0xBD, 0x33 },
+ { 0xBE, 0xAC },
+ { 0xBF, 0x24 },
+ { 0xC0, 0x5D },
+ { 0xC1, 0x94 },
+ { 0xC2, 0xAC },
+ { 0xC3, 0xC5 },
+ { 0xC4, 0xD1 },
+ { 0xC5, 0xDC },
+ { 0xC6, 0xE8 },
+ { 0xC7, 0xED },
+ { 0xC8, 0xF0 },
+ { 0xC9, 0x00 },
+ { 0xCA, 0x00 },
+ { 0xCB, 0x16 },
+ { 0xCC, 0xAF },
+ { 0xCD, 0xFF },
+ { 0xCE, 0xFF },
+ { 0xB0, 0x0A },
+ { 0xB1, 0x00 },
+ { 0xB2, 0x05 },
+ { 0xB3, 0x10 },
+ { 0xB4, 0x22 },
+ { 0xB5, 0x36 },
+ { 0xB6, 0x4A },
+ { 0xB7, 0x6C },
+ { 0xB8, 0x9A },
+ { 0xB9, 0xD7 },
+ { 0xBA, 0x17 },
+ { 0xBB, 0x92 },
+ { 0xBC, 0x15 },
+ { 0xBD, 0x18 },
+ { 0xBE, 0x8C },
+ { 0xBF, 0x00 },
+ { 0xC0, 0x3A },
+ { 0xC1, 0x72 },
+ { 0xC2, 0x8C },
+ { 0xC3, 0xA5 },
+ { 0xC4, 0xB1 },
+ { 0xC5, 0xBE },
+ { 0xC6, 0xCA },
+ { 0xC7, 0xD1 },
+ { 0xC8, 0xD4 },
+ { 0xC9, 0x00 },
+ { 0xCA, 0x00 },
+ { 0xCB, 0x16 },
+ { 0xCC, 0xAF },
+ { 0xCD, 0xFF },
+ { 0xCE, 0xFF },
+ { 0xB0, 0x0B },
+ { 0xB1, 0x04 },
+ { 0xB2, 0x05 },
+ { 0xB3, 0x11 },
+ { 0xB4, 0x24 },
+ { 0xB5, 0x39 },
+ { 0xB6, 0x4E },
+ { 0xB7, 0x72 },
+ { 0xB8, 0xA3 },
+ { 0xB9, 0xE1 },
+ { 0xBA, 0x25 },
+ { 0xBB, 0xA8 },
+ { 0xBC, 0x2E },
+ { 0xBD, 0x32 },
+ { 0xBE, 0xAD },
+ { 0xBF, 0x28 },
+ { 0xC0, 0x63 },
+ { 0xC1, 0x9B },
+ { 0xC2, 0xB5 },
+ { 0xC3, 0xCF },
+ { 0xC4, 0xDB },
+ { 0xC5, 0xE8 },
+ { 0xC6, 0xF5 },
+ { 0xC7, 0xFA },
+ { 0xC8, 0xFC },
+ { 0xC9, 0x00 },
+ { 0xCA, 0x00 },
+ { 0xCB, 0x16 },
+ { 0xCC, 0xAF },
+ { 0xCD, 0xFF },
+ { 0xCE, 0xFF },
+ { 0xB0, 0x0C },
+ { 0xB1, 0x04 },
+ { 0xB2, 0x04 },
+ { 0xB3, 0x0F },
+ { 0xB4, 0x22 },
+ { 0xB5, 0x37 },
+ { 0xB6, 0x4D },
+ { 0xB7, 0x71 },
+ { 0xB8, 0xA2 },
+ { 0xB9, 0xE1 },
+ { 0xBA, 0x26 },
+ { 0xBB, 0xA9 },
+ { 0xBC, 0x2F },
+ { 0xBD, 0x33 },
+ { 0xBE, 0xAC },
+ { 0xBF, 0x24 },
+ { 0xC0, 0x5D },
+ { 0xC1, 0x94 },
+ { 0xC2, 0xAC },
+ { 0xC3, 0xC5 },
+ { 0xC4, 0xD1 },
+ { 0xC5, 0xDC },
+ { 0xC6, 0xE8 },
+ { 0xC7, 0xED },
+ { 0xC8, 0xF0 },
+ { 0xC9, 0x00 },
+ { 0xCA, 0x00 },
+ { 0xCB, 0x16 },
+ { 0xCC, 0xAF },
+ { 0xCD, 0xFF },
+ { 0xCE, 0xFF },
+};
+
+static const struct panel_desc boe_himax8279d10p_panel_desc = {
+ .display_mode = &default_display_mode,
+ .bpc = 8,
+ .width_mm = 135,
+ .height_mm = 216,
+ .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
+ MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM,
+ .format = MIPI_DSI_FMT_RGB888,
+ .lanes = 4,
+ .on_cmds = boe_himax8279d10p_on_cmds,
+ .on_cmds_num = 283,
+};
+
+static const struct of_device_id panel_of_match[] = {
+ {
+ .compatible = "boe,himax8279d8p",
+ .data = &boe_himax8279d8p_panel_desc,
+ },
+ {
+ .compatible = "boe,himax8279d10p",
+ .data = &boe_himax8279d10p_panel_desc,
+ },
+ {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(of, panel_of_match);
+
+static int panel_add(struct panel_info *pinfo)
+{
+ struct device *dev = &pinfo->link->dev;
+ int ret;
+
+ pinfo->pp18_gpio = devm_gpiod_get(dev, "pp18", GPIOD_OUT_HIGH);
+ if (IS_ERR(pinfo->pp18_gpio)) {
+ ret = PTR_ERR(pinfo->pp18_gpio);
+ if (ret != -EPROBE_DEFER)
+ DRM_DEV_ERROR(dev, "failed to get pp18 gpio: %d\n",
+ ret);
+ return ret;
+ }
+
+ pinfo->pp33_gpio = devm_gpiod_get(dev, "pp33", GPIOD_OUT_HIGH);
+ if (IS_ERR(pinfo->pp33_gpio)) {
+ ret = PTR_ERR(pinfo->pp33_gpio);
+ if (ret != -EPROBE_DEFER)
+ DRM_DEV_ERROR(dev, "failed to get pp33 gpio: %d\n",
+ ret);
+ return ret;
+ }
+
+ pinfo->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_HIGH);
+ if (IS_ERR(pinfo->enable_gpio)) {
+ ret = PTR_ERR(pinfo->enable_gpio);
+ if (ret != -EPROBE_DEFER)
+ DRM_DEV_ERROR(dev, "failed to get enable gpio: %d\n",
+ ret);
+ return ret;
+ }
+
+ drm_panel_init(&pinfo->base, dev, &panel_funcs,
+ DRM_MODE_CONNECTOR_DSI);
+
+ ret = drm_panel_of_backlight(&pinfo->base);
+ if (ret)
+ return ret;
+
+ return drm_panel_add(&pinfo->base);
+}
+
+static int panel_probe(struct mipi_dsi_device *dsi)
+{
+ struct panel_info *pinfo;
+ const struct panel_desc *desc;
+ int err;
+
+ pinfo = devm_kzalloc(&dsi->dev, sizeof(*pinfo), GFP_KERNEL);
+ if (!pinfo)
+ return -ENOMEM;
+
+ desc = of_device_get_match_data(&dsi->dev);
+ dsi->mode_flags = desc->mode_flags;
+ dsi->format = desc->format;
+ dsi->lanes = desc->lanes;
+ pinfo->desc = desc;
+
+ pinfo->link = dsi;
+ mipi_dsi_set_drvdata(dsi, pinfo);
+
+ err = panel_add(pinfo);
+ if (err < 0)
+ return err;
+
+ err = mipi_dsi_attach(dsi);
+ if (err < 0)
+ drm_panel_remove(&pinfo->base);
+
+ return err;
+}
+
+static int panel_remove(struct mipi_dsi_device *dsi)
+{
+ struct panel_info *pinfo = mipi_dsi_get_drvdata(dsi);
+ int err;
+
+ err = boe_panel_disable(&pinfo->base);
+ if (err < 0)
+ DRM_DEV_ERROR(&dsi->dev, "failed to disable panel: %d\n",
+ err);
+
+ err = boe_panel_unprepare(&pinfo->base);
+ if (err < 0)
+ DRM_DEV_ERROR(&dsi->dev, "failed to unprepare panel: %d\n",
+ err);
+
+ err = mipi_dsi_detach(dsi);
+ if (err < 0)
+ DRM_DEV_ERROR(&dsi->dev, "failed to detach from DSI host: %d\n",
+ err);
+
+ drm_panel_remove(&pinfo->base);
+
+ return 0;
+}
+
+static void panel_shutdown(struct mipi_dsi_device *dsi)
+{
+ struct panel_info *pinfo = mipi_dsi_get_drvdata(dsi);
+
+ boe_panel_disable(&pinfo->base);
+ boe_panel_unprepare(&pinfo->base);
+}
+
+static struct mipi_dsi_driver panel_driver = {
+ .driver = {
+ .name = "panel-boe-himax8279d",
+ .of_match_table = panel_of_match,
+ },
+ .probe = panel_probe,
+ .remove = panel_remove,
+ .shutdown = panel_shutdown,
+};
+module_mipi_dsi_driver(panel_driver);
+
+MODULE_AUTHOR("Jerry Han <jerry.han.hq@gmail.com>");
+MODULE_DESCRIPTION("Boe Himax8279d driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/panel/panel-feiyang-fy07024di26a30d.c b/drivers/gpu/drm/panel/panel-feiyang-fy07024di26a30d.c
index dabf59e0f56f..95b789ab9d29 100644
--- a/drivers/gpu/drm/panel/panel-feiyang-fy07024di26a30d.c
+++ b/drivers/gpu/drm/panel/panel-feiyang-fy07024di26a30d.c
@@ -9,7 +9,6 @@
#include <drm/drm_panel.h>
#include <drm/drm_print.h>
-#include <linux/backlight.h>
#include <linux/gpio/consumer.h>
#include <linux/delay.h>
#include <linux/module.h>
@@ -22,7 +21,6 @@ struct feiyang {
struct drm_panel panel;
struct mipi_dsi_device *dsi;
- struct backlight_device *backlight;
struct regulator *dvdd;
struct regulator *avdd;
struct gpio_desc *reset;
@@ -102,7 +100,6 @@ static int feiyang_enable(struct drm_panel *panel)
msleep(200);
mipi_dsi_dcs_set_display_on(ctx->dsi);
- backlight_enable(ctx->backlight);
return 0;
}
@@ -111,7 +108,6 @@ static int feiyang_disable(struct drm_panel *panel)
{
struct feiyang *ctx = panel_to_feiyang(panel);
- backlight_disable(ctx->backlight);
return mipi_dsi_dcs_set_display_off(ctx->dsi);
}
@@ -162,13 +158,13 @@ static const struct drm_display_mode feiyang_default_mode = {
.type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
};
-static int feiyang_get_modes(struct drm_panel *panel)
+static int feiyang_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
{
- struct drm_connector *connector = panel->connector;
struct feiyang *ctx = panel_to_feiyang(panel);
struct drm_display_mode *mode;
- mode = drm_mode_duplicate(panel->drm, &feiyang_default_mode);
+ mode = drm_mode_duplicate(connector->dev, &feiyang_default_mode);
if (!mode) {
DRM_DEV_ERROR(&ctx->dsi->dev, "failed to add mode %ux%ux@%u\n",
feiyang_default_mode.hdisplay,
@@ -204,9 +200,8 @@ static int feiyang_dsi_probe(struct mipi_dsi_device *dsi)
mipi_dsi_set_drvdata(dsi, ctx);
ctx->dsi = dsi;
- drm_panel_init(&ctx->panel);
- ctx->panel.dev = &dsi->dev;
- ctx->panel.funcs = &feiyang_funcs;
+ drm_panel_init(&ctx->panel, &dsi->dev, &feiyang_funcs,
+ DRM_MODE_CONNECTOR_DSI);
ctx->dvdd = devm_regulator_get(&dsi->dev, "dvdd");
if (IS_ERR(ctx->dvdd)) {
@@ -226,9 +221,9 @@ static int feiyang_dsi_probe(struct mipi_dsi_device *dsi)
return PTR_ERR(ctx->reset);
}
- ctx->backlight = devm_of_find_backlight(&dsi->dev);
- if (IS_ERR(ctx->backlight))
- return PTR_ERR(ctx->backlight);
+ ret = drm_panel_of_backlight(&ctx->panel);
+ if (ret)
+ return ret;
ret = drm_panel_add(&ctx->panel);
if (ret < 0)
diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9322.c b/drivers/gpu/drm/panel/panel-ilitek-ili9322.c
index 53dd1e128795..f394d53a7da4 100644
--- a/drivers/gpu/drm/panel/panel-ilitek-ili9322.c
+++ b/drivers/gpu/drm/panel/panel-ilitek-ili9322.c
@@ -349,7 +349,6 @@ static const struct regmap_config ili9322_regmap_config = {
static int ili9322_init(struct drm_panel *panel, struct ili9322 *ili)
{
- struct drm_connector *connector = panel->connector;
u8 reg;
int ret;
int i;
@@ -407,23 +406,11 @@ static int ili9322_init(struct drm_panel *panel, struct ili9322 *ili)
* Polarity and inverted color order for RGB input.
* None of this applies in the BT.656 mode.
*/
- if (ili->conf->dclk_active_high) {
+ reg = 0;
+ if (ili->conf->dclk_active_high)
reg = ILI9322_POL_DCLK;
- connector->display_info.bus_flags |=
- DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE;
- } else {
- reg = 0;
- connector->display_info.bus_flags |=
- DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE;
- }
- if (ili->conf->de_active_high) {
+ if (ili->conf->de_active_high)
reg |= ILI9322_POL_DE;
- connector->display_info.bus_flags |=
- DRM_BUS_FLAG_DE_HIGH;
- } else {
- connector->display_info.bus_flags |=
- DRM_BUS_FLAG_DE_LOW;
- }
if (ili->conf->hsync_active_high)
reg |= ILI9322_POL_HSYNC;
if (ili->conf->vsync_active_high)
@@ -654,37 +641,49 @@ static const struct drm_display_mode itu_r_bt_656_720_mode = {
.flags = 0,
};
-static int ili9322_get_modes(struct drm_panel *panel)
+static int ili9322_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
{
- struct drm_connector *connector = panel->connector;
struct ili9322 *ili = panel_to_ili9322(panel);
+ struct drm_device *drm = connector->dev;
struct drm_display_mode *mode;
+ struct drm_display_info *info;
- connector->display_info.width_mm = ili->conf->width_mm;
- connector->display_info.height_mm = ili->conf->height_mm;
+ info = &connector->display_info;
+ info->width_mm = ili->conf->width_mm;
+ info->height_mm = ili->conf->height_mm;
+ if (ili->conf->dclk_active_high)
+ info->bus_flags |= DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE;
+ else
+ info->bus_flags |= DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE;
+
+ if (ili->conf->de_active_high)
+ info->bus_flags |= DRM_BUS_FLAG_DE_HIGH;
+ else
+ info->bus_flags |= DRM_BUS_FLAG_DE_LOW;
switch (ili->input) {
case ILI9322_INPUT_SRGB_DUMMY_320X240:
- mode = drm_mode_duplicate(panel->drm, &srgb_320x240_mode);
+ mode = drm_mode_duplicate(drm, &srgb_320x240_mode);
break;
case ILI9322_INPUT_SRGB_DUMMY_360X240:
- mode = drm_mode_duplicate(panel->drm, &srgb_360x240_mode);
+ mode = drm_mode_duplicate(drm, &srgb_360x240_mode);
break;
case ILI9322_INPUT_PRGB_THROUGH:
case ILI9322_INPUT_PRGB_ALIGNED:
- mode = drm_mode_duplicate(panel->drm, &prgb_320x240_mode);
+ mode = drm_mode_duplicate(drm, &prgb_320x240_mode);
break;
case ILI9322_INPUT_YUV_640X320_YCBCR:
- mode = drm_mode_duplicate(panel->drm, &yuv_640x320_mode);
+ mode = drm_mode_duplicate(drm, &yuv_640x320_mode);
break;
case ILI9322_INPUT_YUV_720X360_YCBCR:
- mode = drm_mode_duplicate(panel->drm, &yuv_720x360_mode);
+ mode = drm_mode_duplicate(drm, &yuv_720x360_mode);
break;
case ILI9322_INPUT_ITU_R_BT656_720X360_YCBCR:
- mode = drm_mode_duplicate(panel->drm, &itu_r_bt_656_720_mode);
+ mode = drm_mode_duplicate(drm, &itu_r_bt_656_720_mode);
break;
case ILI9322_INPUT_ITU_R_BT656_640X320_YCBCR:
- mode = drm_mode_duplicate(panel->drm, &itu_r_bt_656_640_mode);
+ mode = drm_mode_duplicate(drm, &itu_r_bt_656_640_mode);
break;
default:
mode = NULL;
@@ -897,9 +896,8 @@ static int ili9322_probe(struct spi_device *spi)
ili->input = ili->conf->input;
}
- drm_panel_init(&ili->panel);
- ili->panel.dev = dev;
- ili->panel.funcs = &ili9322_drm_funcs;
+ drm_panel_init(&ili->panel, dev, &ili9322_drm_funcs,
+ DRM_MODE_CONNECTOR_DPI);
return drm_panel_add(&ili->panel);
}
diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c b/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c
index 3ad4a46c4e94..f54077c216a3 100644
--- a/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c
+++ b/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c
@@ -3,7 +3,6 @@
* Copyright (C) 2017-2018, Bootlin
*/
-#include <linux/backlight.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/err.h>
@@ -25,7 +24,6 @@ struct ili9881c {
struct drm_panel panel;
struct mipi_dsi_device *dsi;
- struct backlight_device *backlight;
struct regulator *power;
struct gpio_desc *reset;
};
@@ -348,7 +346,6 @@ static int ili9881c_enable(struct drm_panel *panel)
msleep(120);
mipi_dsi_dcs_set_display_on(ctx->dsi);
- backlight_enable(ctx->backlight);
return 0;
}
@@ -357,7 +354,6 @@ static int ili9881c_disable(struct drm_panel *panel)
{
struct ili9881c *ctx = panel_to_ili9881c(panel);
- backlight_disable(ctx->backlight);
return mipi_dsi_dcs_set_display_off(ctx->dsi);
}
@@ -387,13 +383,13 @@ static const struct drm_display_mode bananapi_default_mode = {
.vtotal = 1280 + 10 + 10 + 20,
};
-static int ili9881c_get_modes(struct drm_panel *panel)
+static int ili9881c_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
{
- struct drm_connector *connector = panel->connector;
struct ili9881c *ctx = panel_to_ili9881c(panel);
struct drm_display_mode *mode;
- mode = drm_mode_duplicate(panel->drm, &bananapi_default_mode);
+ mode = drm_mode_duplicate(connector->dev, &bananapi_default_mode);
if (!mode) {
dev_err(&ctx->dsi->dev, "failed to add mode %ux%ux@%u\n",
bananapi_default_mode.hdisplay,
@@ -407,8 +403,8 @@ static int ili9881c_get_modes(struct drm_panel *panel)
mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
drm_mode_probed_add(connector, mode);
- panel->connector->display_info.width_mm = 62;
- panel->connector->display_info.height_mm = 110;
+ connector->display_info.width_mm = 62;
+ connector->display_info.height_mm = 110;
return 1;
}
@@ -423,7 +419,6 @@ static const struct drm_panel_funcs ili9881c_funcs = {
static int ili9881c_dsi_probe(struct mipi_dsi_device *dsi)
{
- struct device_node *np;
struct ili9881c *ctx;
int ret;
@@ -433,9 +428,8 @@ static int ili9881c_dsi_probe(struct mipi_dsi_device *dsi)
mipi_dsi_set_drvdata(dsi, ctx);
ctx->dsi = dsi;
- drm_panel_init(&ctx->panel);
- ctx->panel.dev = &dsi->dev;
- ctx->panel.funcs = &ili9881c_funcs;
+ drm_panel_init(&ctx->panel, &dsi->dev, &ili9881c_funcs,
+ DRM_MODE_CONNECTOR_DSI);
ctx->power = devm_regulator_get(&dsi->dev, "power");
if (IS_ERR(ctx->power)) {
@@ -449,14 +443,9 @@ static int ili9881c_dsi_probe(struct mipi_dsi_device *dsi)
return PTR_ERR(ctx->reset);
}
- np = of_parse_phandle(dsi->dev.of_node, "backlight", 0);
- if (np) {
- ctx->backlight = of_find_backlight_by_node(np);
- of_node_put(np);
-
- if (!ctx->backlight)
- return -EPROBE_DEFER;
- }
+ ret = drm_panel_of_backlight(&ctx->panel);
+ if (ret)
+ return ret;
ret = drm_panel_add(&ctx->panel);
if (ret < 0)
@@ -476,9 +465,6 @@ static int ili9881c_dsi_remove(struct mipi_dsi_device *dsi)
mipi_dsi_detach(dsi);
drm_panel_remove(&ctx->panel);
- if (ctx->backlight)
- put_device(&ctx->backlight->dev);
-
return 0;
}
diff --git a/drivers/gpu/drm/panel/panel-innolux-p079zca.c b/drivers/gpu/drm/panel/panel-innolux-p079zca.c
index d92d1c98878c..7419f1f0acee 100644
--- a/drivers/gpu/drm/panel/panel-innolux-p079zca.c
+++ b/drivers/gpu/drm/panel/panel-innolux-p079zca.c
@@ -3,7 +3,6 @@
* Copyright (c) 2017, Fuzhou Rockchip Electronics Co., Ltd
*/
-#include <linux/backlight.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/module.h>
@@ -52,7 +51,6 @@ struct innolux_panel {
struct mipi_dsi_device *link;
const struct panel_desc *desc;
- struct backlight_device *backlight;
struct regulator_bulk_data *supplies;
struct gpio_desc *enable_gpio;
@@ -72,8 +70,6 @@ static int innolux_panel_disable(struct drm_panel *panel)
if (!innolux->enabled)
return 0;
- backlight_disable(innolux->backlight);
-
innolux->enabled = false;
return 0;
@@ -204,18 +200,10 @@ poweroff:
static int innolux_panel_enable(struct drm_panel *panel)
{
struct innolux_panel *innolux = to_innolux_panel(panel);
- int ret;
if (innolux->enabled)
return 0;
- ret = backlight_enable(innolux->backlight);
- if (ret) {
- DRM_DEV_ERROR(panel->drm->dev,
- "Failed to enable backlight %d\n", ret);
- return ret;
- }
-
innolux->enabled = true;
return 0;
@@ -403,28 +391,27 @@ static const struct panel_desc innolux_p097pfg_panel_desc = {
.sleep_mode_delay = 100, /* T15 */
};
-static int innolux_panel_get_modes(struct drm_panel *panel)
+static int innolux_panel_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
{
struct innolux_panel *innolux = to_innolux_panel(panel);
const struct drm_display_mode *m = innolux->desc->mode;
struct drm_display_mode *mode;
- mode = drm_mode_duplicate(panel->drm, m);
+ mode = drm_mode_duplicate(connector->dev, m);
if (!mode) {
- DRM_DEV_ERROR(panel->drm->dev, "failed to add mode %ux%ux@%u\n",
+ DRM_DEV_ERROR(panel->dev, "failed to add mode %ux%ux@%u\n",
m->hdisplay, m->vdisplay, m->vrefresh);
return -ENOMEM;
}
drm_mode_set_name(mode);
- drm_mode_probed_add(panel->connector, mode);
+ drm_mode_probed_add(connector, mode);
- panel->connector->display_info.width_mm =
- innolux->desc->size.width;
- panel->connector->display_info.height_mm =
- innolux->desc->size.height;
- panel->connector->display_info.bpc = innolux->desc->bpc;
+ connector->display_info.width_mm = innolux->desc->size.width;
+ connector->display_info.height_mm = innolux->desc->size.height;
+ connector->display_info.bpc = innolux->desc->bpc;
return 1;
}
@@ -483,13 +470,12 @@ static int innolux_panel_add(struct mipi_dsi_device *dsi,
innolux->enable_gpio = NULL;
}
- innolux->backlight = devm_of_find_backlight(dev);
- if (IS_ERR(innolux->backlight))
- return PTR_ERR(innolux->backlight);
+ drm_panel_init(&innolux->base, dev, &innolux_panel_funcs,
+ DRM_MODE_CONNECTOR_DSI);
- drm_panel_init(&innolux->base);
- innolux->base.funcs = &innolux_panel_funcs;
- innolux->base.dev = dev;
+ err = drm_panel_of_backlight(&innolux->base);
+ if (err)
+ return err;
err = drm_panel_add(&innolux->base);
if (err < 0)
@@ -528,12 +514,12 @@ static int innolux_panel_remove(struct mipi_dsi_device *dsi)
struct innolux_panel *innolux = mipi_dsi_get_drvdata(dsi);
int err;
- err = innolux_panel_unprepare(&innolux->base);
+ err = drm_panel_unprepare(&innolux->base);
if (err < 0)
DRM_DEV_ERROR(&dsi->dev, "failed to unprepare panel: %d\n",
err);
- err = innolux_panel_disable(&innolux->base);
+ err = drm_panel_disable(&innolux->base);
if (err < 0)
DRM_DEV_ERROR(&dsi->dev, "failed to disable panel: %d\n", err);
@@ -551,8 +537,8 @@ static void innolux_panel_shutdown(struct mipi_dsi_device *dsi)
{
struct innolux_panel *innolux = mipi_dsi_get_drvdata(dsi);
- innolux_panel_unprepare(&innolux->base);
- innolux_panel_disable(&innolux->base);
+ drm_panel_unprepare(&innolux->base);
+ drm_panel_disable(&innolux->base);
}
static struct mipi_dsi_driver innolux_panel_driver = {
diff --git a/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
index ff3e89e61e3f..4bfd8c877c8e 100644
--- a/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
+++ b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
@@ -300,13 +300,14 @@ static const struct drm_display_mode default_mode = {
.flags = 0,
};
-static int jdi_panel_get_modes(struct drm_panel *panel)
+static int jdi_panel_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
{
struct drm_display_mode *mode;
struct jdi_panel *jdi = to_jdi_panel(panel);
struct device *dev = &jdi->dsi->dev;
- mode = drm_mode_duplicate(panel->drm, &default_mode);
+ mode = drm_mode_duplicate(connector->dev, &default_mode);
if (!mode) {
dev_err(dev, "failed to add mode %ux%ux@%u\n",
default_mode.hdisplay, default_mode.vdisplay,
@@ -316,10 +317,10 @@ static int jdi_panel_get_modes(struct drm_panel *panel)
drm_mode_set_name(mode);
- drm_mode_probed_add(panel->connector, mode);
+ drm_mode_probed_add(connector, mode);
- panel->connector->display_info.width_mm = 95;
- panel->connector->display_info.height_mm = 151;
+ connector->display_info.width_mm = 95;
+ connector->display_info.height_mm = 151;
return 1;
}
@@ -437,9 +438,8 @@ static int jdi_panel_add(struct jdi_panel *jdi)
return ret;
}
- drm_panel_init(&jdi->base);
- jdi->base.funcs = &jdi_panel_funcs;
- jdi->base.dev = &jdi->dsi->dev;
+ drm_panel_init(&jdi->base, &jdi->dsi->dev, &jdi_panel_funcs,
+ DRM_MODE_CONNECTOR_DSI);
ret = drm_panel_add(&jdi->base);
diff --git a/drivers/gpu/drm/panel/panel-kingdisplay-kd097d04.c b/drivers/gpu/drm/panel/panel-kingdisplay-kd097d04.c
index 3ac04eb8d0fe..bac1a2a06c92 100644
--- a/drivers/gpu/drm/panel/panel-kingdisplay-kd097d04.c
+++ b/drivers/gpu/drm/panel/panel-kingdisplay-kd097d04.c
@@ -3,7 +3,6 @@
* Copyright (c) 2017, Fuzhou Rockchip Electronics Co., Ltd
*/
-#include <linux/backlight.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/module.h>
@@ -23,7 +22,6 @@ struct kingdisplay_panel {
struct drm_panel base;
struct mipi_dsi_device *link;
- struct backlight_device *backlight;
struct regulator *supply;
struct gpio_desc *enable_gpio;
@@ -191,8 +189,6 @@ static int kingdisplay_panel_disable(struct drm_panel *panel)
if (!kingdisplay->enabled)
return 0;
- backlight_disable(kingdisplay->backlight);
-
err = mipi_dsi_dcs_set_display_off(kingdisplay->link);
if (err < 0)
DRM_DEV_ERROR(panel->dev, "failed to set display off: %d\n",
@@ -303,18 +299,10 @@ poweroff:
static int kingdisplay_panel_enable(struct drm_panel *panel)
{
struct kingdisplay_panel *kingdisplay = to_kingdisplay_panel(panel);
- int ret;
if (kingdisplay->enabled)
return 0;
- ret = backlight_enable(kingdisplay->backlight);
- if (ret) {
- DRM_DEV_ERROR(panel->drm->dev,
- "Failed to enable backlight %d\n", ret);
- return ret;
- }
-
kingdisplay->enabled = true;
return 0;
@@ -333,13 +321,14 @@ static const struct drm_display_mode default_mode = {
.vrefresh = 60,
};
-static int kingdisplay_panel_get_modes(struct drm_panel *panel)
+static int kingdisplay_panel_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
{
struct drm_display_mode *mode;
- mode = drm_mode_duplicate(panel->drm, &default_mode);
+ mode = drm_mode_duplicate(connector->dev, &default_mode);
if (!mode) {
- DRM_DEV_ERROR(panel->drm->dev, "failed to add mode %ux%ux@%u\n",
+ DRM_DEV_ERROR(panel->dev, "failed to add mode %ux%ux@%u\n",
default_mode.hdisplay, default_mode.vdisplay,
default_mode.vrefresh);
return -ENOMEM;
@@ -347,11 +336,11 @@ static int kingdisplay_panel_get_modes(struct drm_panel *panel)
drm_mode_set_name(mode);
- drm_mode_probed_add(panel->connector, mode);
+ drm_mode_probed_add(connector, mode);
- panel->connector->display_info.width_mm = 147;
- panel->connector->display_info.height_mm = 196;
- panel->connector->display_info.bpc = 8;
+ connector->display_info.width_mm = 147;
+ connector->display_info.height_mm = 196;
+ connector->display_info.bpc = 8;
return 1;
}
@@ -387,13 +376,12 @@ static int kingdisplay_panel_add(struct kingdisplay_panel *kingdisplay)
kingdisplay->enable_gpio = NULL;
}
- kingdisplay->backlight = devm_of_find_backlight(dev);
- if (IS_ERR(kingdisplay->backlight))
- return PTR_ERR(kingdisplay->backlight);
+ drm_panel_init(&kingdisplay->base, &kingdisplay->link->dev,
+ &kingdisplay_panel_funcs, DRM_MODE_CONNECTOR_DSI);
- drm_panel_init(&kingdisplay->base);
- kingdisplay->base.funcs = &kingdisplay_panel_funcs;
- kingdisplay->base.dev = &kingdisplay->link->dev;
+ err = drm_panel_of_backlight(&kingdisplay->base);
+ if (err)
+ return err;
return drm_panel_add(&kingdisplay->base);
}
@@ -432,12 +420,12 @@ static int kingdisplay_panel_remove(struct mipi_dsi_device *dsi)
struct kingdisplay_panel *kingdisplay = mipi_dsi_get_drvdata(dsi);
int err;
- err = kingdisplay_panel_unprepare(&kingdisplay->base);
+ err = drm_panel_unprepare(&kingdisplay->base);
if (err < 0)
DRM_DEV_ERROR(&dsi->dev, "failed to unprepare panel: %d\n",
err);
- err = kingdisplay_panel_disable(&kingdisplay->base);
+ err = drm_panel_disable(&kingdisplay->base);
if (err < 0)
DRM_DEV_ERROR(&dsi->dev, "failed to disable panel: %d\n", err);
@@ -455,8 +443,8 @@ static void kingdisplay_panel_shutdown(struct mipi_dsi_device *dsi)
{
struct kingdisplay_panel *kingdisplay = mipi_dsi_get_drvdata(dsi);
- kingdisplay_panel_unprepare(&kingdisplay->base);
- kingdisplay_panel_disable(&kingdisplay->base);
+ drm_panel_unprepare(&kingdisplay->base);
+ drm_panel_disable(&kingdisplay->base);
}
static struct mipi_dsi_driver kingdisplay_panel_driver = {
diff --git a/drivers/gpu/drm/panel/panel-leadtek-ltk500hd1829.c b/drivers/gpu/drm/panel/panel-leadtek-ltk500hd1829.c
new file mode 100644
index 000000000000..76ecf2de9c44
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-leadtek-ltk500hd1829.c
@@ -0,0 +1,531 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2019 Theobroma Systems Design und Consulting GmbH
+ *
+ * base on panel-kingdisplay-kd097d04.c
+ * Copyright (c) 2017, Fuzhou Rockchip Electronics Co., Ltd
+ */
+
+#include <linux/backlight.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
+
+#include <video/mipi_display.h>
+
+#include <drm/drm_crtc.h>
+#include <drm/drm_device.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_print.h>
+
+struct ltk500hd1829 {
+ struct device *dev;
+ struct drm_panel panel;
+ struct gpio_desc *reset_gpio;
+ struct regulator *vcc;
+ struct regulator *iovcc;
+ bool prepared;
+};
+
+struct ltk500hd1829_cmd {
+ char cmd;
+ char data;
+};
+
+/*
+ * There is no description in the Reference Manual about these commands.
+ * We received them from the vendor, so just use them as is.
+ */
+static const struct ltk500hd1829_cmd init_code[] = {
+ { 0xE0, 0x00 },
+ { 0xE1, 0x93 },
+ { 0xE2, 0x65 },
+ { 0xE3, 0xF8 },
+ { 0x80, 0x03 },
+ { 0xE0, 0x04 },
+ { 0x2D, 0x03 },
+ { 0xE0, 0x01 },
+ { 0x00, 0x00 },
+ { 0x01, 0xB6 },
+ { 0x03, 0x00 },
+ { 0x04, 0xC5 },
+ { 0x17, 0x00 },
+ { 0x18, 0xBF },
+ { 0x19, 0x01 },
+ { 0x1A, 0x00 },
+ { 0x1B, 0xBF },
+ { 0x1C, 0x01 },
+ { 0x1F, 0x7C },
+ { 0x20, 0x26 },
+ { 0x21, 0x26 },
+ { 0x22, 0x4E },
+ { 0x37, 0x09 },
+ { 0x38, 0x04 },
+ { 0x39, 0x08 },
+ { 0x3A, 0x1F },
+ { 0x3B, 0x1F },
+ { 0x3C, 0x78 },
+ { 0x3D, 0xFF },
+ { 0x3E, 0xFF },
+ { 0x3F, 0x00 },
+ { 0x40, 0x04 },
+ { 0x41, 0xA0 },
+ { 0x43, 0x0F },
+ { 0x44, 0x0A },
+ { 0x45, 0x24 },
+ { 0x55, 0x01 },
+ { 0x56, 0x01 },
+ { 0x57, 0xA5 },
+ { 0x58, 0x0A },
+ { 0x59, 0x4A },
+ { 0x5A, 0x38 },
+ { 0x5B, 0x10 },
+ { 0x5C, 0x19 },
+ { 0x5D, 0x7C },
+ { 0x5E, 0x64 },
+ { 0x5F, 0x54 },
+ { 0x60, 0x48 },
+ { 0x61, 0x44 },
+ { 0x62, 0x35 },
+ { 0x63, 0x3A },
+ { 0x64, 0x24 },
+ { 0x65, 0x3B },
+ { 0x66, 0x39 },
+ { 0x67, 0x37 },
+ { 0x68, 0x56 },
+ { 0x69, 0x41 },
+ { 0x6A, 0x47 },
+ { 0x6B, 0x2F },
+ { 0x6C, 0x23 },
+ { 0x6D, 0x13 },
+ { 0x6E, 0x02 },
+ { 0x6F, 0x08 },
+ { 0x70, 0x7C },
+ { 0x71, 0x64 },
+ { 0x72, 0x54 },
+ { 0x73, 0x48 },
+ { 0x74, 0x44 },
+ { 0x75, 0x35 },
+ { 0x76, 0x3A },
+ { 0x77, 0x22 },
+ { 0x78, 0x3B },
+ { 0x79, 0x39 },
+ { 0x7A, 0x38 },
+ { 0x7B, 0x52 },
+ { 0x7C, 0x41 },
+ { 0x7D, 0x47 },
+ { 0x7E, 0x2F },
+ { 0x7F, 0x23 },
+ { 0x80, 0x13 },
+ { 0x81, 0x02 },
+ { 0x82, 0x08 },
+ { 0xE0, 0x02 },
+ { 0x00, 0x57 },
+ { 0x01, 0x77 },
+ { 0x02, 0x44 },
+ { 0x03, 0x46 },
+ { 0x04, 0x48 },
+ { 0x05, 0x4A },
+ { 0x06, 0x4C },
+ { 0x07, 0x4E },
+ { 0x08, 0x50 },
+ { 0x09, 0x55 },
+ { 0x0A, 0x52 },
+ { 0x0B, 0x55 },
+ { 0x0C, 0x55 },
+ { 0x0D, 0x55 },
+ { 0x0E, 0x55 },
+ { 0x0F, 0x55 },
+ { 0x10, 0x55 },
+ { 0x11, 0x55 },
+ { 0x12, 0x55 },
+ { 0x13, 0x40 },
+ { 0x14, 0x55 },
+ { 0x15, 0x55 },
+ { 0x16, 0x57 },
+ { 0x17, 0x77 },
+ { 0x18, 0x45 },
+ { 0x19, 0x47 },
+ { 0x1A, 0x49 },
+ { 0x1B, 0x4B },
+ { 0x1C, 0x4D },
+ { 0x1D, 0x4F },
+ { 0x1E, 0x51 },
+ { 0x1F, 0x55 },
+ { 0x20, 0x53 },
+ { 0x21, 0x55 },
+ { 0x22, 0x55 },
+ { 0x23, 0x55 },
+ { 0x24, 0x55 },
+ { 0x25, 0x55 },
+ { 0x26, 0x55 },
+ { 0x27, 0x55 },
+ { 0x28, 0x55 },
+ { 0x29, 0x41 },
+ { 0x2A, 0x55 },
+ { 0x2B, 0x55 },
+ { 0x2C, 0x57 },
+ { 0x2D, 0x77 },
+ { 0x2E, 0x4F },
+ { 0x2F, 0x4D },
+ { 0x30, 0x4B },
+ { 0x31, 0x49 },
+ { 0x32, 0x47 },
+ { 0x33, 0x45 },
+ { 0x34, 0x41 },
+ { 0x35, 0x55 },
+ { 0x36, 0x53 },
+ { 0x37, 0x55 },
+ { 0x38, 0x55 },
+ { 0x39, 0x55 },
+ { 0x3A, 0x55 },
+ { 0x3B, 0x55 },
+ { 0x3C, 0x55 },
+ { 0x3D, 0x55 },
+ { 0x3E, 0x55 },
+ { 0x3F, 0x51 },
+ { 0x40, 0x55 },
+ { 0x41, 0x55 },
+ { 0x42, 0x57 },
+ { 0x43, 0x77 },
+ { 0x44, 0x4E },
+ { 0x45, 0x4C },
+ { 0x46, 0x4A },
+ { 0x47, 0x48 },
+ { 0x48, 0x46 },
+ { 0x49, 0x44 },
+ { 0x4A, 0x40 },
+ { 0x4B, 0x55 },
+ { 0x4C, 0x52 },
+ { 0x4D, 0x55 },
+ { 0x4E, 0x55 },
+ { 0x4F, 0x55 },
+ { 0x50, 0x55 },
+ { 0x51, 0x55 },
+ { 0x52, 0x55 },
+ { 0x53, 0x55 },
+ { 0x54, 0x55 },
+ { 0x55, 0x50 },
+ { 0x56, 0x55 },
+ { 0x57, 0x55 },
+ { 0x58, 0x40 },
+ { 0x59, 0x00 },
+ { 0x5A, 0x00 },
+ { 0x5B, 0x10 },
+ { 0x5C, 0x09 },
+ { 0x5D, 0x30 },
+ { 0x5E, 0x01 },
+ { 0x5F, 0x02 },
+ { 0x60, 0x30 },
+ { 0x61, 0x03 },
+ { 0x62, 0x04 },
+ { 0x63, 0x06 },
+ { 0x64, 0x6A },
+ { 0x65, 0x75 },
+ { 0x66, 0x0F },
+ { 0x67, 0xB3 },
+ { 0x68, 0x0B },
+ { 0x69, 0x06 },
+ { 0x6A, 0x6A },
+ { 0x6B, 0x10 },
+ { 0x6C, 0x00 },
+ { 0x6D, 0x04 },
+ { 0x6E, 0x04 },
+ { 0x6F, 0x88 },
+ { 0x70, 0x00 },
+ { 0x71, 0x00 },
+ { 0x72, 0x06 },
+ { 0x73, 0x7B },
+ { 0x74, 0x00 },
+ { 0x75, 0xBC },
+ { 0x76, 0x00 },
+ { 0x77, 0x05 },
+ { 0x78, 0x2E },
+ { 0x79, 0x00 },
+ { 0x7A, 0x00 },
+ { 0x7B, 0x00 },
+ { 0x7C, 0x00 },
+ { 0x7D, 0x03 },
+ { 0x7E, 0x7B },
+ { 0xE0, 0x04 },
+ { 0x09, 0x10 },
+ { 0x2B, 0x2B },
+ { 0x2E, 0x44 },
+ { 0xE0, 0x00 },
+ { 0xE6, 0x02 },
+ { 0xE7, 0x02 },
+ { 0x35, 0x00 },
+};
+
+static inline
+struct ltk500hd1829 *panel_to_ltk500hd1829(struct drm_panel *panel)
+{
+ return container_of(panel, struct ltk500hd1829, panel);
+}
+
+static int ltk500hd1829_unprepare(struct drm_panel *panel)
+{
+ struct ltk500hd1829 *ctx = panel_to_ltk500hd1829(panel);
+ struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
+ int ret;
+
+ if (!ctx->prepared)
+ return 0;
+
+ ret = mipi_dsi_dcs_set_display_off(dsi);
+ if (ret < 0)
+ DRM_DEV_ERROR(panel->dev, "failed to set display off: %d\n",
+ ret);
+
+ ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
+ if (ret < 0) {
+ DRM_DEV_ERROR(panel->dev, "failed to enter sleep mode: %d\n",
+ ret);
+ }
+
+ /* 120ms to enter sleep mode */
+ msleep(120);
+
+ regulator_disable(ctx->iovcc);
+ regulator_disable(ctx->vcc);
+
+ ctx->prepared = false;
+
+ return 0;
+}
+
+static int ltk500hd1829_prepare(struct drm_panel *panel)
+{
+ struct ltk500hd1829 *ctx = panel_to_ltk500hd1829(panel);
+ struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
+ unsigned int i;
+ int ret;
+
+ if (ctx->prepared)
+ return 0;
+
+ ret = regulator_enable(ctx->vcc);
+ if (ret < 0) {
+ DRM_DEV_ERROR(ctx->dev,
+ "Failed to enable vci 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);
+ /* tRW: 10us */
+ usleep_range(10, 20);
+ gpiod_set_value_cansleep(ctx->reset_gpio, 0);
+
+ /* tRT: >= 5ms */
+ usleep_range(5000, 6000);
+
+ for (i = 0; i < ARRAY_SIZE(init_code); i++) {
+ ret = mipi_dsi_generic_write(dsi, &init_code[i],
+ sizeof(struct ltk500hd1829_cmd));
+ if (ret < 0) {
+ DRM_DEV_ERROR(panel->dev,
+ "failed to write init cmds: %d\n", ret);
+ goto disable_iovcc;
+ }
+ }
+
+ ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
+ if (ret < 0) {
+ DRM_DEV_ERROR(panel->dev, "failed to exit sleep mode: %d\n",
+ ret);
+ goto disable_iovcc;
+ }
+
+ /* 120ms to exit sleep mode */
+ msleep(120);
+
+ ret = mipi_dsi_dcs_set_display_on(dsi);
+ if (ret < 0) {
+ DRM_DEV_ERROR(panel->dev, "failed to set display on: %d\n",
+ ret);
+ goto disable_iovcc;
+ }
+
+ ctx->prepared = true;
+
+ return 0;
+
+disable_iovcc:
+ regulator_disable(ctx->iovcc);
+disable_vcc:
+ regulator_disable(ctx->vcc);
+ return ret;
+}
+
+static const struct drm_display_mode default_mode = {
+ .hdisplay = 720,
+ .hsync_start = 720 + 50,
+ .hsync_end = 720 + 50 + 50,
+ .htotal = 720 + 50 + 50 + 50,
+ .vdisplay = 1280,
+ .vsync_start = 1280 + 30,
+ .vsync_end = 1280 + 30 + 4,
+ .vtotal = 1280 + 30 + 4 + 12,
+ .vrefresh = 60,
+ .clock = 41600,
+ .width_mm = 62,
+ .height_mm = 110,
+};
+
+static int ltk500hd1829_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
+{
+ struct ltk500hd1829 *ctx = panel_to_ltk500hd1829(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%ux@%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 ltk500hd1829_funcs = {
+ .unprepare = ltk500hd1829_unprepare,
+ .prepare = ltk500hd1829_prepare,
+ .get_modes = ltk500hd1829_get_modes,
+};
+
+static int ltk500hd1829_probe(struct mipi_dsi_device *dsi)
+{
+ struct ltk500hd1829 *ctx;
+ struct device *dev = &dsi->dev;
+ int ret;
+
+ ctx = devm_kzalloc(&dsi->dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->reset_gpio = devm_gpiod_get_optional(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);
+ }
+
+ 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;
+ }
+
+ 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_LPM | MIPI_DSI_MODE_EOT_PACKET;
+
+ drm_panel_init(&ctx->panel, &dsi->dev, &ltk500hd1829_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\n", ret);
+ drm_panel_remove(&ctx->panel);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void ltk500hd1829_shutdown(struct mipi_dsi_device *dsi)
+{
+ struct ltk500hd1829 *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 ltk500hd1829_remove(struct mipi_dsi_device *dsi)
+{
+ struct ltk500hd1829 *ctx = mipi_dsi_get_drvdata(dsi);
+ int ret;
+
+ ltk500hd1829_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);
+
+ return 0;
+}
+
+static const struct of_device_id ltk500hd1829_of_match[] = {
+ { .compatible = "leadtek,ltk500hd1829", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, ltk500hd1829_of_match);
+
+static struct mipi_dsi_driver ltk500hd1829_driver = {
+ .driver = {
+ .name = "panel-leadtek-ltk500hd1829",
+ .of_match_table = ltk500hd1829_of_match,
+ },
+ .probe = ltk500hd1829_probe,
+ .remove = ltk500hd1829_remove,
+ .shutdown = ltk500hd1829_shutdown,
+};
+module_mipi_dsi_driver(ltk500hd1829_driver);
+
+MODULE_AUTHOR("Heiko Stuebner <heiko.stuebner@theobroma-systems.com>");
+MODULE_DESCRIPTION("Leadtek LTK500HD1829 panel driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/panel/panel-lg-lb035q02.c b/drivers/gpu/drm/panel/panel-lg-lb035q02.c
new file mode 100644
index 000000000000..e90efeaba4ad
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-lg-lb035q02.c
@@ -0,0 +1,243 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * LG.Philips LB035Q02 LCD Panel Driver
+ *
+ * Copyright (C) 2019 Texas Instruments Incorporated
+ *
+ * Based on the omapdrm-specific panel-lgphilips-lb035q02 driver
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated
+ * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
+ *
+ * Based on a driver by: Steve Sakoman <steve@sakoman.com>
+ */
+
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+
+#include <drm/drm_connector.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_panel.h>
+
+struct lb035q02_device {
+ struct drm_panel panel;
+
+ struct spi_device *spi;
+ struct gpio_desc *enable_gpio;
+};
+
+#define to_lb035q02_device(p) container_of(p, struct lb035q02_device, panel)
+
+static int lb035q02_write(struct lb035q02_device *lcd, u16 reg, u16 val)
+{
+ struct spi_message msg;
+ struct spi_transfer index_xfer = {
+ .len = 3,
+ .cs_change = 1,
+ };
+ struct spi_transfer value_xfer = {
+ .len = 3,
+ };
+ u8 buffer[16];
+
+ spi_message_init(&msg);
+
+ /* register index */
+ buffer[0] = 0x70;
+ buffer[1] = 0x00;
+ buffer[2] = reg & 0x7f;
+ index_xfer.tx_buf = buffer;
+ spi_message_add_tail(&index_xfer, &msg);
+
+ /* register value */
+ buffer[4] = 0x72;
+ buffer[5] = val >> 8;
+ buffer[6] = val;
+ value_xfer.tx_buf = buffer + 4;
+ spi_message_add_tail(&value_xfer, &msg);
+
+ return spi_sync(lcd->spi, &msg);
+}
+
+static int lb035q02_init(struct lb035q02_device *lcd)
+{
+ /* Init sequence from page 28 of the lb035q02 spec. */
+ static const struct {
+ u16 index;
+ u16 value;
+ } init_data[] = {
+ { 0x01, 0x6300 },
+ { 0x02, 0x0200 },
+ { 0x03, 0x0177 },
+ { 0x04, 0x04c7 },
+ { 0x05, 0xffc0 },
+ { 0x06, 0xe806 },
+ { 0x0a, 0x4008 },
+ { 0x0b, 0x0000 },
+ { 0x0d, 0x0030 },
+ { 0x0e, 0x2800 },
+ { 0x0f, 0x0000 },
+ { 0x16, 0x9f80 },
+ { 0x17, 0x0a0f },
+ { 0x1e, 0x00c1 },
+ { 0x30, 0x0300 },
+ { 0x31, 0x0007 },
+ { 0x32, 0x0000 },
+ { 0x33, 0x0000 },
+ { 0x34, 0x0707 },
+ { 0x35, 0x0004 },
+ { 0x36, 0x0302 },
+ { 0x37, 0x0202 },
+ { 0x3a, 0x0a0d },
+ { 0x3b, 0x0806 },
+ };
+
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < ARRAY_SIZE(init_data); ++i) {
+ ret = lb035q02_write(lcd, init_data[i].index,
+ init_data[i].value);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int lb035q02_disable(struct drm_panel *panel)
+{
+ struct lb035q02_device *lcd = to_lb035q02_device(panel);
+
+ gpiod_set_value_cansleep(lcd->enable_gpio, 0);
+
+ return 0;
+}
+
+static int lb035q02_enable(struct drm_panel *panel)
+{
+ struct lb035q02_device *lcd = to_lb035q02_device(panel);
+
+ gpiod_set_value_cansleep(lcd->enable_gpio, 1);
+
+ return 0;
+}
+
+static const struct drm_display_mode lb035q02_mode = {
+ .clock = 6500,
+ .hdisplay = 320,
+ .hsync_start = 320 + 20,
+ .hsync_end = 320 + 20 + 2,
+ .htotal = 320 + 20 + 2 + 68,
+ .vdisplay = 240,
+ .vsync_start = 240 + 4,
+ .vsync_end = 240 + 4 + 2,
+ .vtotal = 240 + 4 + 2 + 18,
+ .vrefresh = 60,
+ .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
+ .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
+ .width_mm = 70,
+ .height_mm = 53,
+};
+
+static int lb035q02_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
+{
+ struct drm_display_mode *mode;
+
+ mode = drm_mode_duplicate(connector->dev, &lb035q02_mode);
+ if (!mode)
+ return -ENOMEM;
+
+ drm_mode_set_name(mode);
+ drm_mode_probed_add(connector, mode);
+
+ connector->display_info.width_mm = lb035q02_mode.width_mm;
+ connector->display_info.height_mm = lb035q02_mode.height_mm;
+ /*
+ * FIXME: According to the datasheet pixel data is sampled on the
+ * rising edge of the clock, but the code running on the Gumstix Overo
+ * Palo35 indicates sampling on the negative edge. This should be
+ * tested on a real device.
+ */
+ connector->display_info.bus_flags = DRM_BUS_FLAG_DE_HIGH
+ | DRM_BUS_FLAG_SYNC_SAMPLE_POSEDGE
+ | DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE;
+
+ return 1;
+}
+
+static const struct drm_panel_funcs lb035q02_funcs = {
+ .disable = lb035q02_disable,
+ .enable = lb035q02_enable,
+ .get_modes = lb035q02_get_modes,
+};
+
+static int lb035q02_probe(struct spi_device *spi)
+{
+ struct lb035q02_device *lcd;
+ int ret;
+
+ lcd = devm_kzalloc(&spi->dev, sizeof(*lcd), GFP_KERNEL);
+ if (!lcd)
+ return -ENOMEM;
+
+ spi_set_drvdata(spi, lcd);
+ lcd->spi = spi;
+
+ lcd->enable_gpio = devm_gpiod_get(&spi->dev, "enable", GPIOD_OUT_LOW);
+ if (IS_ERR(lcd->enable_gpio)) {
+ dev_err(&spi->dev, "failed to parse enable gpio\n");
+ return PTR_ERR(lcd->enable_gpio);
+ }
+
+ ret = lb035q02_init(lcd);
+ if (ret < 0)
+ return ret;
+
+ drm_panel_init(&lcd->panel, &lcd->spi->dev, &lb035q02_funcs,
+ DRM_MODE_CONNECTOR_DPI);
+
+ return drm_panel_add(&lcd->panel);
+}
+
+static int lb035q02_remove(struct spi_device *spi)
+{
+ struct lb035q02_device *lcd = spi_get_drvdata(spi);
+
+ drm_panel_remove(&lcd->panel);
+ drm_panel_disable(&lcd->panel);
+
+ return 0;
+}
+
+static const struct of_device_id lb035q02_of_match[] = {
+ { .compatible = "lgphilips,lb035q02", },
+ { /* sentinel */ },
+};
+
+MODULE_DEVICE_TABLE(of, lb035q02_of_match);
+
+static const struct spi_device_id lb035q02_ids[] = {
+ { "lb035q02", 0 },
+ { /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(spi, lb035q02_ids);
+
+static struct spi_driver lb035q02_driver = {
+ .probe = lb035q02_probe,
+ .remove = lb035q02_remove,
+ .id_table = lb035q02_ids,
+ .driver = {
+ .name = "panel-lg-lb035q02",
+ .of_match_table = lb035q02_of_match,
+ },
+};
+
+module_spi_driver(lb035q02_driver);
+
+MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
+MODULE_DESCRIPTION("LG.Philips LB035Q02 LCD Panel driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/panel/panel-lg-lg4573.c b/drivers/gpu/drm/panel/panel-lg-lg4573.c
index 41bf02d122a1..b262b53dbd85 100644
--- a/drivers/gpu/drm/panel/panel-lg-lg4573.c
+++ b/drivers/gpu/drm/panel/panel-lg-lg4573.c
@@ -42,7 +42,7 @@ static int lg4573_spi_write_u16(struct lg4573 *ctx, u16 data)
struct spi_transfer xfer = {
.len = 2,
};
- u16 temp = cpu_to_be16(data);
+ __be16 temp = cpu_to_be16(data);
struct spi_message msg;
dev_dbg(ctx->panel.dev, "writing data: %x\n", data);
@@ -209,14 +209,14 @@ static const struct drm_display_mode default_mode = {
.vrefresh = 60,
};
-static int lg4573_get_modes(struct drm_panel *panel)
+static int lg4573_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
{
- struct drm_connector *connector = panel->connector;
struct drm_display_mode *mode;
- mode = drm_mode_duplicate(panel->drm, &default_mode);
+ mode = drm_mode_duplicate(connector->dev, &default_mode);
if (!mode) {
- dev_err(panel->drm->dev, "failed to add mode %ux%ux@%u\n",
+ dev_err(panel->dev, "failed to add mode %ux%ux@%u\n",
default_mode.hdisplay, default_mode.vdisplay,
default_mode.vrefresh);
return -ENOMEM;
@@ -227,8 +227,8 @@ static int lg4573_get_modes(struct drm_panel *panel)
mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
drm_mode_probed_add(connector, mode);
- panel->connector->display_info.width_mm = 61;
- panel->connector->display_info.height_mm = 103;
+ connector->display_info.width_mm = 61;
+ connector->display_info.height_mm = 103;
return 1;
}
@@ -259,9 +259,8 @@ static int lg4573_probe(struct spi_device *spi)
return ret;
}
- drm_panel_init(&ctx->panel);
- ctx->panel.dev = &spi->dev;
- ctx->panel.funcs = &lg4573_drm_funcs;
+ drm_panel_init(&ctx->panel, &spi->dev, &lg4573_drm_funcs,
+ DRM_MODE_CONNECTOR_DPI);
return drm_panel_add(&ctx->panel);
}
diff --git a/drivers/gpu/drm/panel/panel-lvds.c b/drivers/gpu/drm/panel/panel-lvds.c
index 1ec57d0806a8..5ce3f4a2b7a1 100644
--- a/drivers/gpu/drm/panel/panel-lvds.c
+++ b/drivers/gpu/drm/panel/panel-lvds.c
@@ -8,7 +8,6 @@
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*/
-#include <linux/backlight.h>
#include <linux/gpio/consumer.h>
#include <linux/module.h>
#include <linux/of_platform.h>
@@ -34,7 +33,6 @@ struct panel_lvds {
unsigned int bus_format;
bool data_mirror;
- struct backlight_device *backlight;
struct regulator *supply;
struct gpio_desc *enable_gpio;
@@ -46,19 +44,6 @@ static inline struct panel_lvds *to_panel_lvds(struct drm_panel *panel)
return container_of(panel, struct panel_lvds, panel);
}
-static int panel_lvds_disable(struct drm_panel *panel)
-{
- struct panel_lvds *lvds = to_panel_lvds(panel);
-
- if (lvds->backlight) {
- lvds->backlight->props.power = FB_BLANK_POWERDOWN;
- lvds->backlight->props.state |= BL_CORE_FBBLANK;
- backlight_update_status(lvds->backlight);
- }
-
- return 0;
-}
-
static int panel_lvds_unprepare(struct drm_panel *panel)
{
struct panel_lvds *lvds = to_panel_lvds(panel);
@@ -93,26 +78,13 @@ static int panel_lvds_prepare(struct drm_panel *panel)
return 0;
}
-static int panel_lvds_enable(struct drm_panel *panel)
+static int panel_lvds_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
{
struct panel_lvds *lvds = to_panel_lvds(panel);
-
- if (lvds->backlight) {
- lvds->backlight->props.state &= ~BL_CORE_FBBLANK;
- lvds->backlight->props.power = FB_BLANK_UNBLANK;
- backlight_update_status(lvds->backlight);
- }
-
- return 0;
-}
-
-static int panel_lvds_get_modes(struct drm_panel *panel)
-{
- struct panel_lvds *lvds = to_panel_lvds(panel);
- struct drm_connector *connector = lvds->panel.connector;
struct drm_display_mode *mode;
- mode = drm_mode_create(lvds->panel.drm);
+ mode = drm_mode_create(connector->dev);
if (!mode)
return 0;
@@ -132,10 +104,8 @@ static int panel_lvds_get_modes(struct drm_panel *panel)
}
static const struct drm_panel_funcs panel_lvds_funcs = {
- .disable = panel_lvds_disable,
.unprepare = panel_lvds_unprepare,
.prepare = panel_lvds_prepare,
- .enable = panel_lvds_enable,
.get_modes = panel_lvds_get_modes,
};
@@ -147,8 +117,11 @@ static int panel_lvds_parse_dt(struct panel_lvds *lvds)
int ret;
ret = of_get_display_timing(np, "panel-timing", &timing);
- if (ret < 0)
+ if (ret < 0) {
+ dev_err(lvds->dev, "%pOF: problems parsing panel-timing (%d)\n",
+ np, ret);
return ret;
+ }
videomode_from_timing(&timing, &lvds->video_mode);
@@ -194,7 +167,6 @@ static int panel_lvds_parse_dt(struct panel_lvds *lvds)
static int panel_lvds_probe(struct platform_device *pdev)
{
struct panel_lvds *lvds;
- struct device_node *np;
int ret;
lvds = devm_kzalloc(&pdev->dev, sizeof(*lvds), GFP_KERNEL);
@@ -240,15 +212,6 @@ static int panel_lvds_probe(struct platform_device *pdev)
return ret;
}
- np = of_parse_phandle(lvds->dev->of_node, "backlight", 0);
- if (np) {
- lvds->backlight = of_find_backlight_by_node(np);
- of_node_put(np);
-
- if (!lvds->backlight)
- return -EPROBE_DEFER;
- }
-
/*
* TODO: Handle all power supplies specified in the DT node in a generic
* way for panels that don't care about power supply ordering. LVDS
@@ -257,20 +220,19 @@ static int panel_lvds_probe(struct platform_device *pdev)
*/
/* Register the panel. */
- drm_panel_init(&lvds->panel);
- lvds->panel.dev = lvds->dev;
- lvds->panel.funcs = &panel_lvds_funcs;
+ drm_panel_init(&lvds->panel, lvds->dev, &panel_lvds_funcs,
+ DRM_MODE_CONNECTOR_LVDS);
+
+ ret = drm_panel_of_backlight(&lvds->panel);
+ if (ret)
+ return ret;
ret = drm_panel_add(&lvds->panel);
if (ret < 0)
- goto error;
+ return ret;
dev_set_drvdata(lvds->dev, lvds);
return 0;
-
-error:
- put_device(&lvds->backlight->dev);
- return ret;
}
static int panel_lvds_remove(struct platform_device *pdev)
@@ -279,10 +241,7 @@ static int panel_lvds_remove(struct platform_device *pdev)
drm_panel_remove(&lvds->panel);
- panel_lvds_disable(&lvds->panel);
-
- if (lvds->backlight)
- put_device(&lvds->backlight->dev);
+ drm_panel_disable(&lvds->panel);
return 0;
}
diff --git a/drivers/gpu/drm/panel/panel-nec-nl8048hl11.c b/drivers/gpu/drm/panel/panel-nec-nl8048hl11.c
new file mode 100644
index 000000000000..c4f83f6384e1
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-nec-nl8048hl11.c
@@ -0,0 +1,254 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * NEC NL8048HL11 Panel Driver
+ *
+ * Copyright (C) 2019 Texas Instruments Incorporated
+ *
+ * Based on the omapdrm-specific panel-nec-nl8048hl11 driver
+ *
+ * Copyright (C) 2010 Texas Instruments Incorporated
+ * Author: Erik Gilling <konkers@android.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/pm.h>
+#include <linux/spi/spi.h>
+
+#include <drm/drm_connector.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_panel.h>
+
+struct nl8048_panel {
+ struct drm_panel panel;
+
+ struct spi_device *spi;
+ struct gpio_desc *reset_gpio;
+};
+
+#define to_nl8048_device(p) container_of(p, struct nl8048_panel, panel)
+
+static int nl8048_write(struct nl8048_panel *lcd, unsigned char addr,
+ unsigned char value)
+{
+ u8 data[4] = { value, 0x01, addr, 0x00 };
+ int ret;
+
+ ret = spi_write(lcd->spi, data, sizeof(data));
+ if (ret)
+ dev_err(&lcd->spi->dev, "SPI write to %u failed: %d\n",
+ addr, ret);
+
+ return ret;
+}
+
+static int nl8048_init(struct nl8048_panel *lcd)
+{
+ static const struct {
+ unsigned char addr;
+ unsigned char data;
+ } nl8048_init_seq[] = {
+ { 3, 0x01 }, { 0, 0x00 }, { 1, 0x01 }, { 4, 0x00 },
+ { 5, 0x14 }, { 6, 0x24 }, { 16, 0xd7 }, { 17, 0x00 },
+ { 18, 0x00 }, { 19, 0x55 }, { 20, 0x01 }, { 21, 0x70 },
+ { 22, 0x1e }, { 23, 0x25 }, { 24, 0x25 }, { 25, 0x02 },
+ { 26, 0x02 }, { 27, 0xa0 }, { 32, 0x2f }, { 33, 0x0f },
+ { 34, 0x0f }, { 35, 0x0f }, { 36, 0x0f }, { 37, 0x0f },
+ { 38, 0x0f }, { 39, 0x00 }, { 40, 0x02 }, { 41, 0x02 },
+ { 42, 0x02 }, { 43, 0x0f }, { 44, 0x0f }, { 45, 0x0f },
+ { 46, 0x0f }, { 47, 0x0f }, { 48, 0x0f }, { 49, 0x0f },
+ { 50, 0x00 }, { 51, 0x02 }, { 52, 0x02 }, { 53, 0x02 },
+ { 80, 0x0c }, { 83, 0x42 }, { 84, 0x42 }, { 85, 0x41 },
+ { 86, 0x14 }, { 89, 0x88 }, { 90, 0x01 }, { 91, 0x00 },
+ { 92, 0x02 }, { 93, 0x0c }, { 94, 0x1c }, { 95, 0x27 },
+ { 98, 0x49 }, { 99, 0x27 }, { 102, 0x76 }, { 103, 0x27 },
+ { 112, 0x01 }, { 113, 0x0e }, { 114, 0x02 }, { 115, 0x0c },
+ { 118, 0x0c }, { 121, 0x30 }, { 130, 0x00 }, { 131, 0x00 },
+ { 132, 0xfc }, { 134, 0x00 }, { 136, 0x00 }, { 138, 0x00 },
+ { 139, 0x00 }, { 140, 0x00 }, { 141, 0xfc }, { 143, 0x00 },
+ { 145, 0x00 }, { 147, 0x00 }, { 148, 0x00 }, { 149, 0x00 },
+ { 150, 0xfc }, { 152, 0x00 }, { 154, 0x00 }, { 156, 0x00 },
+ { 157, 0x00 },
+ };
+
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < ARRAY_SIZE(nl8048_init_seq); ++i) {
+ ret = nl8048_write(lcd, nl8048_init_seq[i].addr,
+ nl8048_init_seq[i].data);
+ if (ret < 0)
+ return ret;
+ }
+
+ udelay(20);
+
+ return nl8048_write(lcd, 2, 0x00);
+}
+
+static int nl8048_disable(struct drm_panel *panel)
+{
+ struct nl8048_panel *lcd = to_nl8048_device(panel);
+
+ gpiod_set_value_cansleep(lcd->reset_gpio, 0);
+
+ return 0;
+}
+
+static int nl8048_enable(struct drm_panel *panel)
+{
+ struct nl8048_panel *lcd = to_nl8048_device(panel);
+
+ gpiod_set_value_cansleep(lcd->reset_gpio, 1);
+
+ return 0;
+}
+
+static const struct drm_display_mode nl8048_mode = {
+ /* NEC PIX Clock Ratings MIN:21.8MHz TYP:23.8MHz MAX:25.7MHz */
+ .clock = 23800,
+ .hdisplay = 800,
+ .hsync_start = 800 + 6,
+ .hsync_end = 800 + 6 + 1,
+ .htotal = 800 + 6 + 1 + 4,
+ .vdisplay = 480,
+ .vsync_start = 480 + 3,
+ .vsync_end = 480 + 3 + 1,
+ .vtotal = 480 + 3 + 1 + 4,
+ .vrefresh = 60,
+ .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
+ .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
+ .width_mm = 89,
+ .height_mm = 53,
+};
+
+static int nl8048_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
+{
+ struct drm_display_mode *mode;
+
+ mode = drm_mode_duplicate(connector->dev, &nl8048_mode);
+ if (!mode)
+ return -ENOMEM;
+
+ drm_mode_set_name(mode);
+ drm_mode_probed_add(connector, mode);
+
+ connector->display_info.width_mm = nl8048_mode.width_mm;
+ connector->display_info.height_mm = nl8048_mode.height_mm;
+ connector->display_info.bus_flags = DRM_BUS_FLAG_DE_HIGH
+ | DRM_BUS_FLAG_SYNC_SAMPLE_NEGEDGE
+ | DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE;
+
+ return 1;
+}
+
+static const struct drm_panel_funcs nl8048_funcs = {
+ .disable = nl8048_disable,
+ .enable = nl8048_enable,
+ .get_modes = nl8048_get_modes,
+};
+
+static int __maybe_unused nl8048_suspend(struct device *dev)
+{
+ struct nl8048_panel *lcd = dev_get_drvdata(dev);
+
+ nl8048_write(lcd, 2, 0x01);
+ msleep(40);
+
+ return 0;
+}
+
+static int __maybe_unused nl8048_resume(struct device *dev)
+{
+ struct nl8048_panel *lcd = dev_get_drvdata(dev);
+
+ /* Reinitialize the panel. */
+ spi_setup(lcd->spi);
+ nl8048_write(lcd, 2, 0x00);
+ nl8048_init(lcd);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(nl8048_pm_ops, nl8048_suspend, nl8048_resume);
+
+static int nl8048_probe(struct spi_device *spi)
+{
+ struct nl8048_panel *lcd;
+ int ret;
+
+ lcd = devm_kzalloc(&spi->dev, sizeof(*lcd), GFP_KERNEL);
+ if (!lcd)
+ return -ENOMEM;
+
+ spi_set_drvdata(spi, lcd);
+ lcd->spi = spi;
+
+ lcd->reset_gpio = devm_gpiod_get(&spi->dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(lcd->reset_gpio)) {
+ dev_err(&spi->dev, "failed to parse reset gpio\n");
+ return PTR_ERR(lcd->reset_gpio);
+ }
+
+ spi->mode = SPI_MODE_0;
+ spi->bits_per_word = 32;
+
+ ret = spi_setup(spi);
+ if (ret < 0) {
+ dev_err(&spi->dev, "failed to setup SPI: %d\n", ret);
+ return ret;
+ }
+
+ ret = nl8048_init(lcd);
+ if (ret < 0)
+ return ret;
+
+ drm_panel_init(&lcd->panel, &lcd->spi->dev, &nl8048_funcs,
+ DRM_MODE_CONNECTOR_DPI);
+
+ return drm_panel_add(&lcd->panel);
+}
+
+static int nl8048_remove(struct spi_device *spi)
+{
+ struct nl8048_panel *lcd = spi_get_drvdata(spi);
+
+ drm_panel_remove(&lcd->panel);
+ drm_panel_disable(&lcd->panel);
+ drm_panel_unprepare(&lcd->panel);
+
+ return 0;
+}
+
+static const struct of_device_id nl8048_of_match[] = {
+ { .compatible = "nec,nl8048hl11", },
+ { /* sentinel */ },
+};
+
+MODULE_DEVICE_TABLE(of, nl8048_of_match);
+
+static const struct spi_device_id nl8048_ids[] = {
+ { "nl8048hl11", 0 },
+ { /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(spi, nl8048_ids);
+
+static struct spi_driver nl8048_driver = {
+ .probe = nl8048_probe,
+ .remove = nl8048_remove,
+ .id_table = nl8048_ids,
+ .driver = {
+ .name = "panel-nec-nl8048hl11",
+ .pm = &nl8048_pm_ops,
+ .of_match_table = nl8048_of_match,
+ },
+};
+
+module_spi_driver(nl8048_driver);
+
+MODULE_AUTHOR("Erik Gilling <konkers@android.com>");
+MODULE_DESCRIPTION("NEC-NL8048HL11 Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/panel/panel-novatek-nt39016.c b/drivers/gpu/drm/panel/panel-novatek-nt39016.c
new file mode 100644
index 000000000000..a470810f7dbe
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-novatek-nt39016.c
@@ -0,0 +1,358 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Novatek NT39016 TFT LCD panel driver
+ *
+ * Copyright (C) 2017, Maarten ter Huurne <maarten@treewalker.org>
+ * Copyright (C) 2019, Paul Cercueil <paul@crapouillou.net>
+ */
+
+#include <linux/backlight.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/media-bus-format.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+
+#include <drm/drm_modes.h>
+#include <drm/drm_panel.h>
+
+enum nt39016_regs {
+ NT39016_REG_SYSTEM,
+ NT39016_REG_TIMING,
+ NT39016_REG_OP,
+ NT39016_REG_DATA_IN,
+ NT39016_REG_SRC_TIMING_DELAY,
+ NT39016_REG_GATE_TIMING_DELAY,
+ NT39016_REG_RESERVED,
+ NT39016_REG_INITIAL_FUNC,
+ NT39016_REG_CONTRAST,
+ NT39016_REG_BRIGHTNESS,
+ NT39016_REG_HUE_SATURATION,
+ NT39016_REG_RB_SUBCONTRAST,
+ NT39016_REG_R_SUBBRIGHTNESS,
+ NT39016_REG_B_SUBBRIGHTNESS,
+ NT39016_REG_VCOMDC,
+ NT39016_REG_VCOMAC,
+ NT39016_REG_VGAM2,
+ NT39016_REG_VGAM34,
+ NT39016_REG_VGAM56,
+ NT39016_REG_VCOMDC_TRIM = 0x1e,
+ NT39016_REG_DISPLAY_MODE = 0x20,
+};
+
+#define NT39016_SYSTEM_RESET_N BIT(0)
+#define NT39016_SYSTEM_STANDBY BIT(1)
+
+struct nt39016_panel_info {
+ struct drm_display_mode display_mode;
+ u16 width_mm, height_mm;
+ u32 bus_format, bus_flags;
+};
+
+struct nt39016 {
+ struct drm_panel drm_panel;
+ struct device *dev;
+ struct regmap *map;
+ struct regulator *supply;
+ const struct nt39016_panel_info *panel_info;
+
+ struct gpio_desc *reset_gpio;
+
+ struct backlight_device *backlight;
+};
+
+static inline struct nt39016 *to_nt39016(struct drm_panel *panel)
+{
+ return container_of(panel, struct nt39016, drm_panel);
+}
+
+#define RV(REG, VAL) { .reg = (REG), .def = (VAL), .delay_us = 2 }
+static const struct reg_sequence nt39016_panel_regs[] = {
+ RV(NT39016_REG_SYSTEM, 0x00),
+ RV(NT39016_REG_TIMING, 0x00),
+ RV(NT39016_REG_OP, 0x03),
+ RV(NT39016_REG_DATA_IN, 0xCC),
+ RV(NT39016_REG_SRC_TIMING_DELAY, 0x46),
+ RV(NT39016_REG_GATE_TIMING_DELAY, 0x05),
+ RV(NT39016_REG_RESERVED, 0x00),
+ RV(NT39016_REG_INITIAL_FUNC, 0x00),
+ RV(NT39016_REG_CONTRAST, 0x08),
+ RV(NT39016_REG_BRIGHTNESS, 0x40),
+ RV(NT39016_REG_HUE_SATURATION, 0x88),
+ RV(NT39016_REG_RB_SUBCONTRAST, 0x88),
+ RV(NT39016_REG_R_SUBBRIGHTNESS, 0x20),
+ RV(NT39016_REG_B_SUBBRIGHTNESS, 0x20),
+ RV(NT39016_REG_VCOMDC, 0x67),
+ RV(NT39016_REG_VCOMAC, 0xA4),
+ RV(NT39016_REG_VGAM2, 0x04),
+ RV(NT39016_REG_VGAM34, 0x24),
+ RV(NT39016_REG_VGAM56, 0x24),
+ RV(NT39016_REG_DISPLAY_MODE, 0x00),
+};
+
+#undef RV
+
+static const struct regmap_range nt39016_regmap_no_ranges[] = {
+ regmap_reg_range(0x13, 0x1D),
+ regmap_reg_range(0x1F, 0x1F),
+};
+
+static const struct regmap_access_table nt39016_regmap_access_table = {
+ .no_ranges = nt39016_regmap_no_ranges,
+ .n_no_ranges = ARRAY_SIZE(nt39016_regmap_no_ranges),
+};
+
+static const struct regmap_config nt39016_regmap_config = {
+ .reg_bits = 6,
+ .pad_bits = 2,
+ .val_bits = 8,
+
+ .max_register = NT39016_REG_DISPLAY_MODE,
+ .wr_table = &nt39016_regmap_access_table,
+ .write_flag_mask = 0x02,
+
+ .cache_type = REGCACHE_FLAT,
+};
+
+static int nt39016_prepare(struct drm_panel *drm_panel)
+{
+ struct nt39016 *panel = to_nt39016(drm_panel);
+ int err;
+
+ err = regulator_enable(panel->supply);
+ if (err) {
+ dev_err(panel->dev, "Failed to enable power supply: %d", err);
+ return err;
+ }
+
+ /*
+ * Reset the NT39016.
+ * The documentation says the reset pulse should be at least 40 us to
+ * pass the glitch filter, but when testing I see some resets fail and
+ * some succeed when using a 70 us delay, so we use 100 us instead.
+ */
+ gpiod_set_value_cansleep(panel->reset_gpio, 1);
+ usleep_range(100, 1000);
+ gpiod_set_value_cansleep(panel->reset_gpio, 0);
+ udelay(2);
+
+ /* Init all registers. */
+ err = regmap_multi_reg_write(panel->map, nt39016_panel_regs,
+ ARRAY_SIZE(nt39016_panel_regs));
+ if (err) {
+ dev_err(panel->dev, "Failed to init registers: %d", err);
+ goto err_disable_regulator;
+ }
+
+ return 0;
+
+err_disable_regulator:
+ regulator_disable(panel->supply);
+ return err;
+}
+
+static int nt39016_unprepare(struct drm_panel *drm_panel)
+{
+ struct nt39016 *panel = to_nt39016(drm_panel);
+
+ gpiod_set_value_cansleep(panel->reset_gpio, 1);
+
+ regulator_disable(panel->supply);
+
+ return 0;
+}
+
+static int nt39016_enable(struct drm_panel *drm_panel)
+{
+ struct nt39016 *panel = to_nt39016(drm_panel);
+ int ret;
+
+ ret = regmap_write(panel->map, NT39016_REG_SYSTEM,
+ NT39016_SYSTEM_RESET_N | NT39016_SYSTEM_STANDBY);
+ if (ret) {
+ dev_err(panel->dev, "Unable to enable panel: %d", ret);
+ return ret;
+ }
+
+ if (panel->backlight) {
+ /* Wait for the picture to be ready before enabling backlight */
+ msleep(150);
+
+ ret = backlight_enable(panel->backlight);
+ }
+
+ return ret;
+}
+
+static int nt39016_disable(struct drm_panel *drm_panel)
+{
+ struct nt39016 *panel = to_nt39016(drm_panel);
+ int err;
+
+ backlight_disable(panel->backlight);
+
+ err = regmap_write(panel->map, NT39016_REG_SYSTEM,
+ NT39016_SYSTEM_RESET_N);
+ if (err) {
+ dev_err(panel->dev, "Unable to disable panel: %d", err);
+ return err;
+ }
+
+ return 0;
+}
+
+static int nt39016_get_modes(struct drm_panel *drm_panel,
+ struct drm_connector *connector)
+{
+ struct nt39016 *panel = to_nt39016(drm_panel);
+ const struct nt39016_panel_info *panel_info = panel->panel_info;
+ struct drm_display_mode *mode;
+
+ mode = drm_mode_duplicate(connector->dev, &panel_info->display_mode);
+ if (!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.bpc = 8;
+ connector->display_info.width_mm = panel_info->width_mm;
+ connector->display_info.height_mm = panel_info->height_mm;
+
+ drm_display_info_set_bus_formats(&connector->display_info,
+ &panel_info->bus_format, 1);
+ connector->display_info.bus_flags = panel_info->bus_flags;
+
+ return 1;
+}
+
+static const struct drm_panel_funcs nt39016_funcs = {
+ .prepare = nt39016_prepare,
+ .unprepare = nt39016_unprepare,
+ .enable = nt39016_enable,
+ .disable = nt39016_disable,
+ .get_modes = nt39016_get_modes,
+};
+
+static int nt39016_probe(struct spi_device *spi)
+{
+ struct device *dev = &spi->dev;
+ struct nt39016 *panel;
+ int err;
+
+ panel = devm_kzalloc(dev, sizeof(*panel), GFP_KERNEL);
+ if (!panel)
+ return -ENOMEM;
+
+ panel->dev = dev;
+ spi_set_drvdata(spi, panel);
+
+ panel->panel_info = of_device_get_match_data(dev);
+ if (!panel->panel_info)
+ return -EINVAL;
+
+ panel->supply = devm_regulator_get(dev, "power");
+ if (IS_ERR(panel->supply)) {
+ dev_err(dev, "Failed to get power supply");
+ return PTR_ERR(panel->supply);
+ }
+
+ panel->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(panel->reset_gpio)) {
+ dev_err(dev, "Failed to get reset GPIO");
+ return PTR_ERR(panel->reset_gpio);
+ }
+
+ spi->bits_per_word = 8;
+ spi->mode = SPI_MODE_3 | SPI_3WIRE;
+ err = spi_setup(spi);
+ if (err) {
+ dev_err(dev, "Failed to setup SPI");
+ return err;
+ }
+
+ panel->map = devm_regmap_init_spi(spi, &nt39016_regmap_config);
+ if (IS_ERR(panel->map)) {
+ dev_err(dev, "Failed to init regmap");
+ return PTR_ERR(panel->map);
+ }
+
+ panel->backlight = devm_of_find_backlight(dev);
+ if (IS_ERR(panel->backlight)) {
+ err = PTR_ERR(panel->backlight);
+ if (err != -EPROBE_DEFER)
+ dev_err(dev, "Failed to get backlight handle");
+ return err;
+ }
+
+ drm_panel_init(&panel->drm_panel, dev, &nt39016_funcs,
+ DRM_MODE_CONNECTOR_DPI);
+
+ err = drm_panel_add(&panel->drm_panel);
+ if (err < 0) {
+ dev_err(dev, "Failed to register panel");
+ return err;
+ }
+
+ return 0;
+}
+
+static int nt39016_remove(struct spi_device *spi)
+{
+ struct nt39016 *panel = spi_get_drvdata(spi);
+
+ drm_panel_remove(&panel->drm_panel);
+
+ nt39016_disable(&panel->drm_panel);
+ nt39016_unprepare(&panel->drm_panel);
+
+ return 0;
+}
+
+static const struct nt39016_panel_info kd035g6_info = {
+ .display_mode = {
+ .clock = 6000,
+ .hdisplay = 320,
+ .hsync_start = 320 + 10,
+ .hsync_end = 320 + 10 + 50,
+ .htotal = 320 + 10 + 50 + 20,
+ .vdisplay = 240,
+ .vsync_start = 240 + 5,
+ .vsync_end = 240 + 5 + 1,
+ .vtotal = 240 + 5 + 1 + 4,
+ .vrefresh = 60,
+ .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
+ },
+ .width_mm = 71,
+ .height_mm = 53,
+ .bus_format = MEDIA_BUS_FMT_RGB888_1X24,
+ .bus_flags = DRM_BUS_FLAG_PIXDATA_NEGEDGE,
+};
+
+static const struct of_device_id nt39016_of_match[] = {
+ { .compatible = "kingdisplay,kd035g6-54nt", .data = &kd035g6_info },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, nt39016_of_match);
+
+static struct spi_driver nt39016_driver = {
+ .driver = {
+ .name = "nt39016",
+ .of_match_table = nt39016_of_match,
+ },
+ .probe = nt39016_probe,
+ .remove = nt39016_remove,
+};
+
+module_spi_driver(nt39016_driver);
+
+MODULE_AUTHOR("Maarten ter Huurne <maarten@treewalker.org>");
+MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/panel/panel-olimex-lcd-olinuxino.c b/drivers/gpu/drm/panel/panel-olimex-lcd-olinuxino.c
index 2bae1db3ff34..09deb99981a4 100644
--- a/drivers/gpu/drm/panel/panel-olimex-lcd-olinuxino.c
+++ b/drivers/gpu/drm/panel/panel-olimex-lcd-olinuxino.c
@@ -6,7 +6,6 @@
* Author: Stefan Mavrodiev <stefan@olimex.com>
*/
-#include <linux/backlight.h>
#include <linux/crc32.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
@@ -68,7 +67,6 @@ struct lcd_olinuxino {
bool prepared;
bool enabled;
- struct backlight_device *backlight;
struct regulator *supply;
struct gpio_desc *enable_gpio;
@@ -87,8 +85,6 @@ static int lcd_olinuxino_disable(struct drm_panel *panel)
if (!lcd->enabled)
return 0;
- backlight_disable(lcd->backlight);
-
lcd->enabled = false;
return 0;
@@ -134,19 +130,16 @@ static int lcd_olinuxino_enable(struct drm_panel *panel)
if (lcd->enabled)
return 0;
- backlight_enable(lcd->backlight);
-
lcd->enabled = true;
return 0;
}
-static int lcd_olinuxino_get_modes(struct drm_panel *panel)
+static int lcd_olinuxino_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
{
struct lcd_olinuxino *lcd = to_lcd_olinuxino(panel);
- struct drm_connector *connector = lcd->panel.connector;
struct lcd_olinuxino_info *lcd_info = &lcd->eeprom.info;
- struct drm_device *drm = lcd->panel.drm;
struct lcd_olinuxino_mode *lcd_mode;
struct drm_display_mode *mode;
u32 i, num = 0;
@@ -155,13 +148,13 @@ static int lcd_olinuxino_get_modes(struct drm_panel *panel)
lcd_mode = (struct lcd_olinuxino_mode *)
&lcd->eeprom.reserved[i * sizeof(*lcd_mode)];
- mode = drm_mode_create(drm);
+ mode = drm_mode_create(connector->dev);
if (!mode) {
- dev_err(drm->dev, "failed to add mode %ux%u@%u\n",
+ dev_err(panel->dev, "failed to add mode %ux%u@%u\n",
lcd_mode->hactive,
lcd_mode->vactive,
lcd_mode->refresh);
- continue;
+ continue;
}
mode->clock = lcd_mode->pixelclock;
@@ -284,13 +277,12 @@ static int lcd_olinuxino_probe(struct i2c_client *client,
if (IS_ERR(lcd->enable_gpio))
return PTR_ERR(lcd->enable_gpio);
- lcd->backlight = devm_of_find_backlight(dev);
- if (IS_ERR(lcd->backlight))
- return PTR_ERR(lcd->backlight);
+ drm_panel_init(&lcd->panel, dev, &lcd_olinuxino_funcs,
+ DRM_MODE_CONNECTOR_DPI);
- drm_panel_init(&lcd->panel);
- lcd->panel.dev = dev;
- lcd->panel.funcs = &lcd_olinuxino_funcs;
+ ret = drm_panel_of_backlight(&lcd->panel);
+ if (ret)
+ return ret;
return drm_panel_add(&lcd->panel);
}
@@ -301,8 +293,8 @@ static int lcd_olinuxino_remove(struct i2c_client *client)
drm_panel_remove(&panel->panel);
- lcd_olinuxino_disable(&panel->panel);
- lcd_olinuxino_unprepare(&panel->panel);
+ drm_panel_disable(&panel->panel);
+ drm_panel_unprepare(&panel->panel);
return 0;
}
diff --git a/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c b/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c
index c7b48df8869a..bb0c992171e8 100644
--- a/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c
+++ b/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c
@@ -349,11 +349,12 @@ static int otm8009a_enable(struct drm_panel *panel)
return 0;
}
-static int otm8009a_get_modes(struct drm_panel *panel)
+static int otm8009a_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
{
struct drm_display_mode *mode;
- mode = drm_mode_duplicate(panel->drm, &default_mode);
+ mode = drm_mode_duplicate(connector->dev, &default_mode);
if (!mode) {
DRM_ERROR("failed to add mode %ux%ux@%u\n",
default_mode.hdisplay, default_mode.vdisplay,
@@ -364,10 +365,10 @@ static int otm8009a_get_modes(struct drm_panel *panel)
drm_mode_set_name(mode);
mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
- drm_mode_probed_add(panel->connector, mode);
+ drm_mode_probed_add(connector, mode);
- panel->connector->display_info.width_mm = mode->width_mm;
- panel->connector->display_info.height_mm = mode->height_mm;
+ connector->display_info.width_mm = mode->width_mm;
+ connector->display_info.height_mm = mode->height_mm;
return 1;
}
@@ -455,9 +456,8 @@ static int otm8009a_probe(struct mipi_dsi_device *dsi)
dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
MIPI_DSI_MODE_LPM;
- drm_panel_init(&ctx->panel);
- ctx->panel.dev = dev;
- ctx->panel.funcs = &otm8009a_drm_funcs;
+ drm_panel_init(&ctx->panel, dev, &otm8009a_drm_funcs,
+ DRM_MODE_CONNECTOR_DSI);
ctx->bl_dev = devm_backlight_device_register(dev, dev_name(dev),
dsi->host->dev, ctx,
diff --git a/drivers/gpu/drm/panel/panel-osd-osd101t2587-53ts.c b/drivers/gpu/drm/panel/panel-osd-osd101t2587-53ts.c
index e0e20ecff916..3a0229d60095 100644
--- a/drivers/gpu/drm/panel/panel-osd-osd101t2587-53ts.c
+++ b/drivers/gpu/drm/panel/panel-osd-osd101t2587-53ts.c
@@ -4,7 +4,6 @@
* Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
*/
-#include <linux/backlight.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/regulator/consumer.h>
@@ -20,7 +19,6 @@ struct osd101t2587_panel {
struct drm_panel base;
struct mipi_dsi_device *dsi;
- struct backlight_device *backlight;
struct regulator *supply;
bool prepared;
@@ -42,8 +40,6 @@ static int osd101t2587_panel_disable(struct drm_panel *panel)
if (!osd101t2587->enabled)
return 0;
- backlight_disable(osd101t2587->backlight);
-
ret = mipi_dsi_shutdown_peripheral(osd101t2587->dsi);
osd101t2587->enabled = false;
@@ -91,8 +87,6 @@ static int osd101t2587_panel_enable(struct drm_panel *panel)
if (ret)
return ret;
- backlight_enable(osd101t2587->backlight);
-
osd101t2587->enabled = true;
return ret;
@@ -112,14 +106,15 @@ static const struct drm_display_mode default_mode_osd101t2587 = {
.flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
};
-static int osd101t2587_panel_get_modes(struct drm_panel *panel)
+static int osd101t2587_panel_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
{
struct osd101t2587_panel *osd101t2587 = ti_osd_panel(panel);
struct drm_display_mode *mode;
- mode = drm_mode_duplicate(panel->drm, osd101t2587->default_mode);
+ mode = drm_mode_duplicate(connector->dev, osd101t2587->default_mode);
if (!mode) {
- dev_err(panel->drm->dev, "failed to add mode %ux%ux@%u\n",
+ dev_err(panel->dev, "failed to add mode %ux%ux@%u\n",
osd101t2587->default_mode->hdisplay,
osd101t2587->default_mode->vdisplay,
osd101t2587->default_mode->vrefresh);
@@ -128,10 +123,10 @@ static int osd101t2587_panel_get_modes(struct drm_panel *panel)
drm_mode_set_name(mode);
- drm_mode_probed_add(panel->connector, mode);
+ drm_mode_probed_add(connector, mode);
- panel->connector->display_info.width_mm = 217;
- panel->connector->display_info.height_mm = 136;
+ connector->display_info.width_mm = 217;
+ connector->display_info.height_mm = 136;
return 1;
}
@@ -157,18 +152,18 @@ MODULE_DEVICE_TABLE(of, osd101t2587_of_match);
static int osd101t2587_panel_add(struct osd101t2587_panel *osd101t2587)
{
struct device *dev = &osd101t2587->dsi->dev;
+ int ret;
osd101t2587->supply = devm_regulator_get(dev, "power");
if (IS_ERR(osd101t2587->supply))
return PTR_ERR(osd101t2587->supply);
- osd101t2587->backlight = devm_of_find_backlight(dev);
- if (IS_ERR(osd101t2587->backlight))
- return PTR_ERR(osd101t2587->backlight);
+ drm_panel_init(&osd101t2587->base, &osd101t2587->dsi->dev,
+ &osd101t2587_panel_funcs, DRM_MODE_CONNECTOR_DSI);
- drm_panel_init(&osd101t2587->base);
- osd101t2587->base.funcs = &osd101t2587_panel_funcs;
- osd101t2587->base.dev = &osd101t2587->dsi->dev;
+ ret = drm_panel_of_backlight(&osd101t2587->base);
+ if (ret)
+ return ret;
return drm_panel_add(&osd101t2587->base);
}
@@ -215,12 +210,11 @@ static int osd101t2587_panel_remove(struct mipi_dsi_device *dsi)
struct osd101t2587_panel *osd101t2587 = mipi_dsi_get_drvdata(dsi);
int ret;
- ret = osd101t2587_panel_disable(&osd101t2587->base);
+ ret = drm_panel_disable(&osd101t2587->base);
if (ret < 0)
dev_warn(&dsi->dev, "failed to disable panel: %d\n", ret);
- osd101t2587_panel_unprepare(&osd101t2587->base);
-
+ drm_panel_unprepare(&osd101t2587->base);
drm_panel_remove(&osd101t2587->base);
ret = mipi_dsi_detach(dsi);
@@ -234,8 +228,8 @@ static void osd101t2587_panel_shutdown(struct mipi_dsi_device *dsi)
{
struct osd101t2587_panel *osd101t2587 = mipi_dsi_get_drvdata(dsi);
- osd101t2587_panel_disable(&osd101t2587->base);
- osd101t2587_panel_unprepare(&osd101t2587->base);
+ drm_panel_disable(&osd101t2587->base);
+ drm_panel_unprepare(&osd101t2587->base);
}
static struct mipi_dsi_driver osd101t2587_panel_driver = {
diff --git a/drivers/gpu/drm/panel/panel-panasonic-vvx10f034n00.c b/drivers/gpu/drm/panel/panel-panasonic-vvx10f034n00.c
index 3dff0b3f73c2..69693451462e 100644
--- a/drivers/gpu/drm/panel/panel-panasonic-vvx10f034n00.c
+++ b/drivers/gpu/drm/panel/panel-panasonic-vvx10f034n00.c
@@ -7,7 +7,6 @@
* Based on AUO panel driver by Rob Clark <robdclark@gmail.com>
*/
-#include <linux/backlight.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/of.h>
@@ -31,7 +30,6 @@ struct wuxga_nt_panel {
struct drm_panel base;
struct mipi_dsi_device *dsi;
- struct backlight_device *backlight;
struct regulator *supply;
bool prepared;
@@ -62,12 +60,6 @@ static int wuxga_nt_panel_disable(struct drm_panel *panel)
mipi_ret = mipi_dsi_shutdown_peripheral(wuxga_nt->dsi);
- if (wuxga_nt->backlight) {
- wuxga_nt->backlight->props.power = FB_BLANK_POWERDOWN;
- wuxga_nt->backlight->props.state |= BL_CORE_FBBLANK;
- bl_ret = backlight_update_status(wuxga_nt->backlight);
- }
-
wuxga_nt->enabled = false;
return mipi_ret ? mipi_ret : bl_ret;
@@ -142,12 +134,6 @@ static int wuxga_nt_panel_enable(struct drm_panel *panel)
if (wuxga_nt->enabled)
return 0;
- if (wuxga_nt->backlight) {
- wuxga_nt->backlight->props.power = FB_BLANK_UNBLANK;
- wuxga_nt->backlight->props.state &= ~BL_CORE_FBBLANK;
- backlight_update_status(wuxga_nt->backlight);
- }
-
wuxga_nt->enabled = true;
return 0;
@@ -166,24 +152,25 @@ static const struct drm_display_mode default_mode = {
.vrefresh = 60,
};
-static int wuxga_nt_panel_get_modes(struct drm_panel *panel)
+static int wuxga_nt_panel_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
{
struct drm_display_mode *mode;
- mode = drm_mode_duplicate(panel->drm, &default_mode);
+ mode = drm_mode_duplicate(connector->dev, &default_mode);
if (!mode) {
- dev_err(panel->drm->dev, "failed to add mode %ux%ux@%u\n",
- default_mode.hdisplay, default_mode.vdisplay,
- default_mode.vrefresh);
+ dev_err(panel->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);
- drm_mode_probed_add(panel->connector, mode);
+ drm_mode_probed_add(connector, mode);
- panel->connector->display_info.width_mm = 217;
- panel->connector->display_info.height_mm = 136;
+ connector->display_info.width_mm = 217;
+ connector->display_info.height_mm = 136;
return 1;
}
@@ -205,7 +192,6 @@ MODULE_DEVICE_TABLE(of, wuxga_nt_of_match);
static int wuxga_nt_panel_add(struct wuxga_nt_panel *wuxga_nt)
{
struct device *dev = &wuxga_nt->dsi->dev;
- struct device_node *np;
int ret;
wuxga_nt->mode = &default_mode;
@@ -214,39 +200,20 @@ static int wuxga_nt_panel_add(struct wuxga_nt_panel *wuxga_nt)
if (IS_ERR(wuxga_nt->supply))
return PTR_ERR(wuxga_nt->supply);
- np = of_parse_phandle(dev->of_node, "backlight", 0);
- if (np) {
- wuxga_nt->backlight = of_find_backlight_by_node(np);
- of_node_put(np);
-
- if (!wuxga_nt->backlight)
- return -EPROBE_DEFER;
- }
-
- drm_panel_init(&wuxga_nt->base);
- wuxga_nt->base.funcs = &wuxga_nt_panel_funcs;
- wuxga_nt->base.dev = &wuxga_nt->dsi->dev;
+ drm_panel_init(&wuxga_nt->base, &wuxga_nt->dsi->dev,
+ &wuxga_nt_panel_funcs, DRM_MODE_CONNECTOR_DSI);
- ret = drm_panel_add(&wuxga_nt->base);
- if (ret < 0)
- goto put_backlight;
-
- return 0;
-
-put_backlight:
- if (wuxga_nt->backlight)
- put_device(&wuxga_nt->backlight->dev);
+ ret = drm_panel_of_backlight(&wuxga_nt->base);
+ if (ret)
+ return ret;
- return ret;
+ return drm_panel_add(&wuxga_nt->base);
}
static void wuxga_nt_panel_del(struct wuxga_nt_panel *wuxga_nt)
{
if (wuxga_nt->base.dev)
drm_panel_remove(&wuxga_nt->base);
-
- if (wuxga_nt->backlight)
- put_device(&wuxga_nt->backlight->dev);
}
static int wuxga_nt_panel_probe(struct mipi_dsi_device *dsi)
@@ -281,7 +248,7 @@ static int wuxga_nt_panel_remove(struct mipi_dsi_device *dsi)
struct wuxga_nt_panel *wuxga_nt = mipi_dsi_get_drvdata(dsi);
int ret;
- ret = wuxga_nt_panel_disable(&wuxga_nt->base);
+ ret = drm_panel_disable(&wuxga_nt->base);
if (ret < 0)
dev_err(&dsi->dev, "failed to disable panel: %d\n", ret);
@@ -298,7 +265,7 @@ static void wuxga_nt_panel_shutdown(struct mipi_dsi_device *dsi)
{
struct wuxga_nt_panel *wuxga_nt = mipi_dsi_get_drvdata(dsi);
- wuxga_nt_panel_disable(&wuxga_nt->base);
+ drm_panel_disable(&wuxga_nt->base);
}
static struct mipi_dsi_driver wuxga_nt_panel_driver = {
diff --git a/drivers/gpu/drm/panel/panel-raspberrypi-touchscreen.c b/drivers/gpu/drm/panel/panel-raspberrypi-touchscreen.c
index 28c0620dfe0f..8f078b7dd89e 100644
--- a/drivers/gpu/drm/panel/panel-raspberrypi-touchscreen.c
+++ b/drivers/gpu/drm/panel/panel-raspberrypi-touchscreen.c
@@ -44,8 +44,6 @@
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/fb.h>
-#include <linux/gpio.h>
-#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/of.h>
@@ -311,10 +309,9 @@ static int rpi_touchscreen_enable(struct drm_panel *panel)
return 0;
}
-static int rpi_touchscreen_get_modes(struct drm_panel *panel)
+static int rpi_touchscreen_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
{
- struct drm_connector *connector = panel->connector;
- struct drm_device *drm = panel->drm;
unsigned int i, num = 0;
static const u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
@@ -322,9 +319,9 @@ static int rpi_touchscreen_get_modes(struct drm_panel *panel)
const struct drm_display_mode *m = &rpi_touchscreen_modes[i];
struct drm_display_mode *mode;
- mode = drm_mode_duplicate(drm, m);
+ mode = drm_mode_duplicate(connector->dev, m);
if (!mode) {
- dev_err(drm->dev, "failed to add mode %ux%u@%u\n",
+ dev_err(panel->dev, "failed to add mode %ux%u@%u\n",
m->hdisplay, m->vdisplay, m->vrefresh);
continue;
}
@@ -399,7 +396,13 @@ static int rpi_touchscreen_probe(struct i2c_client *i2c,
/* Look up the DSI host. It needs to probe before we do. */
endpoint = of_graph_get_next_endpoint(dev->of_node, NULL);
+ if (!endpoint)
+ return -ENODEV;
+
dsi_host_node = of_graph_get_remote_port_parent(endpoint);
+ if (!dsi_host_node)
+ goto error;
+
host = of_find_mipi_dsi_host_by_node(dsi_host_node);
of_node_put(dsi_host_node);
if (!host) {
@@ -408,6 +411,9 @@ static int rpi_touchscreen_probe(struct i2c_client *i2c,
}
info.node = of_graph_get_remote_port(endpoint);
+ if (!info.node)
+ goto error;
+
of_node_put(endpoint);
ts->dsi = mipi_dsi_device_register_full(host, &info);
@@ -417,8 +423,8 @@ static int rpi_touchscreen_probe(struct i2c_client *i2c,
return PTR_ERR(ts->dsi);
}
- ts->base.dev = dev;
- ts->base.funcs = &rpi_touchscreen_funcs;
+ drm_panel_init(&ts->base, dev, &rpi_touchscreen_funcs,
+ DRM_MODE_CONNECTOR_DSI);
/* This appears last, as it's what will unblock the DSI host
* driver's component bind function.
@@ -428,6 +434,10 @@ static int rpi_touchscreen_probe(struct i2c_client *i2c,
return ret;
return 0;
+
+error:
+ of_node_put(endpoint);
+ return -ENODEV;
}
static int rpi_touchscreen_remove(struct i2c_client *i2c)
diff --git a/drivers/gpu/drm/panel/panel-raydium-rm67191.c b/drivers/gpu/drm/panel/panel-raydium-rm67191.c
new file mode 100644
index 000000000000..313637d53d28
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-raydium-rm67191.c
@@ -0,0 +1,667 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Raydium RM67191 MIPI-DSI panel driver
+ *
+ * Copyright 2019 NXP
+ */
+
+#include <linux/backlight.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
+
+#include <video/mipi_display.h>
+#include <video/of_videomode.h>
+#include <video/videomode.h>
+
+#include <drm/drm_crtc.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_print.h>
+
+/* Panel specific color-format bits */
+#define COL_FMT_16BPP 0x55
+#define COL_FMT_18BPP 0x66
+#define COL_FMT_24BPP 0x77
+
+/* Write Manufacture Command Set Control */
+#define WRMAUCCTR 0xFE
+
+/* Manufacturer Command Set pages (CMD2) */
+struct cmd_set_entry {
+ u8 cmd;
+ u8 param;
+};
+
+/*
+ * There is no description in the Reference Manual about these commands.
+ * We received them from vendor, so just use them as is.
+ */
+static const struct cmd_set_entry manufacturer_cmd_set[] = {
+ {0xFE, 0x0B},
+ {0x28, 0x40},
+ {0x29, 0x4F},
+ {0xFE, 0x0E},
+ {0x4B, 0x00},
+ {0x4C, 0x0F},
+ {0x4D, 0x20},
+ {0x4E, 0x40},
+ {0x4F, 0x60},
+ {0x50, 0xA0},
+ {0x51, 0xC0},
+ {0x52, 0xE0},
+ {0x53, 0xFF},
+ {0xFE, 0x0D},
+ {0x18, 0x08},
+ {0x42, 0x00},
+ {0x08, 0x41},
+ {0x46, 0x02},
+ {0x72, 0x09},
+ {0xFE, 0x0A},
+ {0x24, 0x17},
+ {0x04, 0x07},
+ {0x1A, 0x0C},
+ {0x0F, 0x44},
+ {0xFE, 0x04},
+ {0x00, 0x0C},
+ {0x05, 0x08},
+ {0x06, 0x08},
+ {0x08, 0x08},
+ {0x09, 0x08},
+ {0x0A, 0xE6},
+ {0x0B, 0x8C},
+ {0x1A, 0x12},
+ {0x1E, 0xE0},
+ {0x29, 0x93},
+ {0x2A, 0x93},
+ {0x2F, 0x02},
+ {0x31, 0x02},
+ {0x33, 0x05},
+ {0x37, 0x2D},
+ {0x38, 0x2D},
+ {0x3A, 0x1E},
+ {0x3B, 0x1E},
+ {0x3D, 0x27},
+ {0x3F, 0x80},
+ {0x40, 0x40},
+ {0x41, 0xE0},
+ {0x4F, 0x2F},
+ {0x50, 0x1E},
+ {0xFE, 0x06},
+ {0x00, 0xCC},
+ {0x05, 0x05},
+ {0x07, 0xA2},
+ {0x08, 0xCC},
+ {0x0D, 0x03},
+ {0x0F, 0xA2},
+ {0x32, 0xCC},
+ {0x37, 0x05},
+ {0x39, 0x83},
+ {0x3A, 0xCC},
+ {0x41, 0x04},
+ {0x43, 0x83},
+ {0x44, 0xCC},
+ {0x49, 0x05},
+ {0x4B, 0xA2},
+ {0x4C, 0xCC},
+ {0x51, 0x03},
+ {0x53, 0xA2},
+ {0x75, 0xCC},
+ {0x7A, 0x03},
+ {0x7C, 0x83},
+ {0x7D, 0xCC},
+ {0x82, 0x02},
+ {0x84, 0x83},
+ {0x85, 0xEC},
+ {0x86, 0x0F},
+ {0x87, 0xFF},
+ {0x88, 0x00},
+ {0x8A, 0x02},
+ {0x8C, 0xA2},
+ {0x8D, 0xEA},
+ {0x8E, 0x01},
+ {0x8F, 0xE8},
+ {0xFE, 0x06},
+ {0x90, 0x0A},
+ {0x92, 0x06},
+ {0x93, 0xA0},
+ {0x94, 0xA8},
+ {0x95, 0xEC},
+ {0x96, 0x0F},
+ {0x97, 0xFF},
+ {0x98, 0x00},
+ {0x9A, 0x02},
+ {0x9C, 0xA2},
+ {0xAC, 0x04},
+ {0xFE, 0x06},
+ {0xB1, 0x12},
+ {0xB2, 0x17},
+ {0xB3, 0x17},
+ {0xB4, 0x17},
+ {0xB5, 0x17},
+ {0xB6, 0x11},
+ {0xB7, 0x08},
+ {0xB8, 0x09},
+ {0xB9, 0x06},
+ {0xBA, 0x07},
+ {0xBB, 0x17},
+ {0xBC, 0x17},
+ {0xBD, 0x17},
+ {0xBE, 0x17},
+ {0xBF, 0x17},
+ {0xC0, 0x17},
+ {0xC1, 0x17},
+ {0xC2, 0x17},
+ {0xC3, 0x17},
+ {0xC4, 0x0F},
+ {0xC5, 0x0E},
+ {0xC6, 0x00},
+ {0xC7, 0x01},
+ {0xC8, 0x10},
+ {0xFE, 0x06},
+ {0x95, 0xEC},
+ {0x8D, 0xEE},
+ {0x44, 0xEC},
+ {0x4C, 0xEC},
+ {0x32, 0xEC},
+ {0x3A, 0xEC},
+ {0x7D, 0xEC},
+ {0x75, 0xEC},
+ {0x00, 0xEC},
+ {0x08, 0xEC},
+ {0x85, 0xEC},
+ {0xA6, 0x21},
+ {0xA7, 0x05},
+ {0xA9, 0x06},
+ {0x82, 0x06},
+ {0x41, 0x06},
+ {0x7A, 0x07},
+ {0x37, 0x07},
+ {0x05, 0x06},
+ {0x49, 0x06},
+ {0x0D, 0x04},
+ {0x51, 0x04},
+};
+
+static const u32 rad_bus_formats[] = {
+ MEDIA_BUS_FMT_RGB888_1X24,
+ MEDIA_BUS_FMT_RGB666_1X18,
+ MEDIA_BUS_FMT_RGB565_1X16,
+};
+
+static const u32 rad_bus_flags = DRM_BUS_FLAG_DE_LOW |
+ DRM_BUS_FLAG_PIXDATA_NEGEDGE;
+
+struct rad_panel {
+ struct drm_panel panel;
+ struct mipi_dsi_device *dsi;
+
+ struct gpio_desc *reset;
+ struct backlight_device *backlight;
+
+ struct regulator_bulk_data *supplies;
+ unsigned int num_supplies;
+
+ bool prepared;
+ bool enabled;
+};
+
+static const struct drm_display_mode default_mode = {
+ .clock = 132000,
+ .hdisplay = 1080,
+ .hsync_start = 1080 + 20,
+ .hsync_end = 1080 + 20 + 2,
+ .htotal = 1080 + 20 + 2 + 34,
+ .vdisplay = 1920,
+ .vsync_start = 1920 + 10,
+ .vsync_end = 1920 + 10 + 2,
+ .vtotal = 1920 + 10 + 2 + 4,
+ .vrefresh = 60,
+ .width_mm = 68,
+ .height_mm = 121,
+ .flags = DRM_MODE_FLAG_NHSYNC |
+ DRM_MODE_FLAG_NVSYNC,
+};
+
+static inline struct rad_panel *to_rad_panel(struct drm_panel *panel)
+{
+ return container_of(panel, struct rad_panel, panel);
+}
+
+static int rad_panel_push_cmd_list(struct mipi_dsi_device *dsi)
+{
+ size_t i;
+ size_t count = ARRAY_SIZE(manufacturer_cmd_set);
+ int ret = 0;
+
+ for (i = 0; i < count; i++) {
+ const struct cmd_set_entry *entry = &manufacturer_cmd_set[i];
+ u8 buffer[2] = { entry->cmd, entry->param };
+
+ ret = mipi_dsi_generic_write(dsi, &buffer, sizeof(buffer));
+ if (ret < 0)
+ return ret;
+ }
+
+ return ret;
+};
+
+static int color_format_from_dsi_format(enum mipi_dsi_pixel_format format)
+{
+ switch (format) {
+ case MIPI_DSI_FMT_RGB565:
+ return COL_FMT_16BPP;
+ case MIPI_DSI_FMT_RGB666:
+ case MIPI_DSI_FMT_RGB666_PACKED:
+ return COL_FMT_18BPP;
+ case MIPI_DSI_FMT_RGB888:
+ return COL_FMT_24BPP;
+ default:
+ return COL_FMT_24BPP; /* for backward compatibility */
+ }
+};
+
+static int rad_panel_prepare(struct drm_panel *panel)
+{
+ struct rad_panel *rad = to_rad_panel(panel);
+ int ret;
+
+ if (rad->prepared)
+ return 0;
+
+ ret = regulator_bulk_enable(rad->num_supplies, rad->supplies);
+ if (ret)
+ return ret;
+
+ if (rad->reset) {
+ gpiod_set_value_cansleep(rad->reset, 1);
+ usleep_range(3000, 5000);
+ gpiod_set_value_cansleep(rad->reset, 0);
+ usleep_range(18000, 20000);
+ }
+
+ rad->prepared = true;
+
+ return 0;
+}
+
+static int rad_panel_unprepare(struct drm_panel *panel)
+{
+ struct rad_panel *rad = to_rad_panel(panel);
+ int ret;
+
+ if (!rad->prepared)
+ return 0;
+
+ /*
+ * Right after asserting the reset, we need to release it, so that the
+ * touch driver can have an active connection with the touch controller
+ * even after the display is turned off.
+ */
+ if (rad->reset) {
+ gpiod_set_value_cansleep(rad->reset, 1);
+ usleep_range(15000, 17000);
+ gpiod_set_value_cansleep(rad->reset, 0);
+ }
+
+ ret = regulator_bulk_disable(rad->num_supplies, rad->supplies);
+ if (ret)
+ return ret;
+
+ rad->prepared = false;
+
+ return 0;
+}
+
+static int rad_panel_enable(struct drm_panel *panel)
+{
+ struct rad_panel *rad = to_rad_panel(panel);
+ struct mipi_dsi_device *dsi = rad->dsi;
+ struct device *dev = &dsi->dev;
+ int color_format = color_format_from_dsi_format(dsi->format);
+ int ret;
+
+ if (rad->enabled)
+ return 0;
+
+ dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+ ret = rad_panel_push_cmd_list(dsi);
+ if (ret < 0) {
+ DRM_DEV_ERROR(dev, "Failed to send MCS (%d)\n", ret);
+ goto fail;
+ }
+
+ /* Select User Command Set table (CMD1) */
+ ret = mipi_dsi_generic_write(dsi, (u8[]){ WRMAUCCTR, 0x00 }, 2);
+ if (ret < 0)
+ goto fail;
+
+ /* Software reset */
+ ret = mipi_dsi_dcs_soft_reset(dsi);
+ if (ret < 0) {
+ DRM_DEV_ERROR(dev, "Failed to do Software Reset (%d)\n", ret);
+ goto fail;
+ }
+
+ usleep_range(15000, 17000);
+
+ /* Set DSI mode */
+ ret = mipi_dsi_generic_write(dsi, (u8[]){ 0xC2, 0x0B }, 2);
+ if (ret < 0) {
+ DRM_DEV_ERROR(dev, "Failed to set DSI mode (%d)\n", ret);
+ goto fail;
+ }
+ /* Set tear ON */
+ ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
+ if (ret < 0) {
+ DRM_DEV_ERROR(dev, "Failed to set tear ON (%d)\n", ret);
+ goto fail;
+ }
+ /* Set tear scanline */
+ ret = mipi_dsi_dcs_set_tear_scanline(dsi, 0x380);
+ if (ret < 0) {
+ DRM_DEV_ERROR(dev, "Failed to set tear scanline (%d)\n", ret);
+ goto fail;
+ }
+ /* Set pixel format */
+ ret = mipi_dsi_dcs_set_pixel_format(dsi, color_format);
+ DRM_DEV_DEBUG_DRIVER(dev, "Interface color format set to 0x%x\n",
+ color_format);
+ if (ret < 0) {
+ DRM_DEV_ERROR(dev, "Failed to set pixel format (%d)\n", ret);
+ goto fail;
+ }
+ /* Exit sleep mode */
+ ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
+ if (ret < 0) {
+ DRM_DEV_ERROR(dev, "Failed to exit sleep mode (%d)\n", ret);
+ goto fail;
+ }
+
+ usleep_range(5000, 7000);
+
+ ret = mipi_dsi_dcs_set_display_on(dsi);
+ if (ret < 0) {
+ DRM_DEV_ERROR(dev, "Failed to set display ON (%d)\n", ret);
+ goto fail;
+ }
+
+ backlight_enable(rad->backlight);
+
+ rad->enabled = true;
+
+ return 0;
+
+fail:
+ gpiod_set_value_cansleep(rad->reset, 1);
+
+ return ret;
+}
+
+static int rad_panel_disable(struct drm_panel *panel)
+{
+ struct rad_panel *rad = to_rad_panel(panel);
+ struct mipi_dsi_device *dsi = rad->dsi;
+ struct device *dev = &dsi->dev;
+ int ret;
+
+ if (!rad->enabled)
+ return 0;
+
+ dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+ backlight_disable(rad->backlight);
+
+ usleep_range(10000, 12000);
+
+ ret = mipi_dsi_dcs_set_display_off(dsi);
+ if (ret < 0) {
+ DRM_DEV_ERROR(dev, "Failed to set display OFF (%d)\n", ret);
+ return ret;
+ }
+
+ usleep_range(5000, 10000);
+
+ ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
+ if (ret < 0) {
+ DRM_DEV_ERROR(dev, "Failed to enter sleep mode (%d)\n", ret);
+ return ret;
+ }
+
+ rad->enabled = false;
+
+ return 0;
+}
+
+static int rad_panel_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
+{
+ struct drm_display_mode *mode;
+
+ mode = drm_mode_duplicate(connector->dev, &default_mode);
+ if (!mode) {
+ DRM_DEV_ERROR(panel->dev, "failed to add mode %ux%ux@%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;
+ drm_mode_probed_add(connector, mode);
+
+ connector->display_info.width_mm = mode->width_mm;
+ connector->display_info.height_mm = mode->height_mm;
+ connector->display_info.bus_flags = rad_bus_flags;
+
+ drm_display_info_set_bus_formats(&connector->display_info,
+ rad_bus_formats,
+ ARRAY_SIZE(rad_bus_formats));
+ return 1;
+}
+
+static int rad_bl_get_brightness(struct backlight_device *bl)
+{
+ struct mipi_dsi_device *dsi = bl_get_data(bl);
+ struct rad_panel *rad = mipi_dsi_get_drvdata(dsi);
+ u16 brightness;
+ int ret;
+
+ if (!rad->prepared)
+ return 0;
+
+ dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
+
+ ret = mipi_dsi_dcs_get_display_brightness(dsi, &brightness);
+ if (ret < 0)
+ return ret;
+
+ bl->props.brightness = brightness;
+
+ return brightness & 0xff;
+}
+
+static int rad_bl_update_status(struct backlight_device *bl)
+{
+ struct mipi_dsi_device *dsi = bl_get_data(bl);
+ struct rad_panel *rad = mipi_dsi_get_drvdata(dsi);
+ int ret = 0;
+
+ if (!rad->prepared)
+ return 0;
+
+ dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
+
+ ret = mipi_dsi_dcs_set_display_brightness(dsi, bl->props.brightness);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static const struct backlight_ops rad_bl_ops = {
+ .update_status = rad_bl_update_status,
+ .get_brightness = rad_bl_get_brightness,
+};
+
+static const struct drm_panel_funcs rad_panel_funcs = {
+ .prepare = rad_panel_prepare,
+ .unprepare = rad_panel_unprepare,
+ .enable = rad_panel_enable,
+ .disable = rad_panel_disable,
+ .get_modes = rad_panel_get_modes,
+};
+
+static const char * const rad_supply_names[] = {
+ "v3p3",
+ "v1p8",
+};
+
+static int rad_init_regulators(struct rad_panel *rad)
+{
+ struct device *dev = &rad->dsi->dev;
+ int i;
+
+ rad->num_supplies = ARRAY_SIZE(rad_supply_names);
+ rad->supplies = devm_kcalloc(dev, rad->num_supplies,
+ sizeof(*rad->supplies), GFP_KERNEL);
+ if (!rad->supplies)
+ return -ENOMEM;
+
+ for (i = 0; i < rad->num_supplies; i++)
+ rad->supplies[i].supply = rad_supply_names[i];
+
+ return devm_regulator_bulk_get(dev, rad->num_supplies, rad->supplies);
+};
+
+static int rad_panel_probe(struct mipi_dsi_device *dsi)
+{
+ struct device *dev = &dsi->dev;
+ struct device_node *np = dev->of_node;
+ struct rad_panel *panel;
+ struct backlight_properties bl_props;
+ int ret;
+ u32 video_mode;
+
+ panel = devm_kzalloc(&dsi->dev, sizeof(*panel), GFP_KERNEL);
+ if (!panel)
+ return -ENOMEM;
+
+ mipi_dsi_set_drvdata(dsi, panel);
+
+ panel->dsi = dsi;
+
+ dsi->format = MIPI_DSI_FMT_RGB888;
+ dsi->mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO |
+ MIPI_DSI_CLOCK_NON_CONTINUOUS;
+
+ ret = of_property_read_u32(np, "video-mode", &video_mode);
+ if (!ret) {
+ switch (video_mode) {
+ case 0:
+ /* burst mode */
+ dsi->mode_flags |= MIPI_DSI_MODE_VIDEO_BURST;
+ break;
+ case 1:
+ /* non-burst mode with sync event */
+ break;
+ case 2:
+ /* non-burst mode with sync pulse */
+ dsi->mode_flags |= MIPI_DSI_MODE_VIDEO_SYNC_PULSE;
+ break;
+ default:
+ dev_warn(dev, "invalid video mode %d\n", video_mode);
+ break;
+ }
+ }
+
+ ret = of_property_read_u32(np, "dsi-lanes", &dsi->lanes);
+ if (ret) {
+ dev_err(dev, "Failed to get dsi-lanes property (%d)\n", ret);
+ return ret;
+ }
+
+ panel->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(panel->reset))
+ return PTR_ERR(panel->reset);
+
+ memset(&bl_props, 0, sizeof(bl_props));
+ bl_props.type = BACKLIGHT_RAW;
+ bl_props.brightness = 255;
+ bl_props.max_brightness = 255;
+
+ panel->backlight = devm_backlight_device_register(dev, dev_name(dev),
+ dev, dsi, &rad_bl_ops,
+ &bl_props);
+ if (IS_ERR(panel->backlight)) {
+ ret = PTR_ERR(panel->backlight);
+ dev_err(dev, "Failed to register backlight (%d)\n", ret);
+ return ret;
+ }
+
+ ret = rad_init_regulators(panel);
+ if (ret)
+ return ret;
+
+ drm_panel_init(&panel->panel, dev, &rad_panel_funcs,
+ DRM_MODE_CONNECTOR_DSI);
+ dev_set_drvdata(dev, panel);
+
+ ret = drm_panel_add(&panel->panel);
+ if (ret)
+ return ret;
+
+ ret = mipi_dsi_attach(dsi);
+ if (ret)
+ drm_panel_remove(&panel->panel);
+
+ return ret;
+}
+
+static int rad_panel_remove(struct mipi_dsi_device *dsi)
+{
+ struct rad_panel *rad = mipi_dsi_get_drvdata(dsi);
+ struct device *dev = &dsi->dev;
+ int ret;
+
+ ret = mipi_dsi_detach(dsi);
+ if (ret)
+ DRM_DEV_ERROR(dev, "Failed to detach from host (%d)\n",
+ ret);
+
+ drm_panel_remove(&rad->panel);
+
+ return 0;
+}
+
+static void rad_panel_shutdown(struct mipi_dsi_device *dsi)
+{
+ struct rad_panel *rad = mipi_dsi_get_drvdata(dsi);
+
+ rad_panel_disable(&rad->panel);
+ rad_panel_unprepare(&rad->panel);
+}
+
+static const struct of_device_id rad_of_match[] = {
+ { .compatible = "raydium,rm67191", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, rad_of_match);
+
+static struct mipi_dsi_driver rad_panel_driver = {
+ .driver = {
+ .name = "panel-raydium-rm67191",
+ .of_match_table = rad_of_match,
+ },
+ .probe = rad_panel_probe,
+ .remove = rad_panel_remove,
+ .shutdown = rad_panel_shutdown,
+};
+module_mipi_dsi_driver(rad_panel_driver);
+
+MODULE_AUTHOR("Robert Chiras <robert.chiras@nxp.com>");
+MODULE_DESCRIPTION("DRM Driver for Raydium RM67191 MIPI DSI panel");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/panel/panel-raydium-rm68200.c b/drivers/gpu/drm/panel/panel-raydium-rm68200.c
index ba889625ad43..e8982948e0ea 100644
--- a/drivers/gpu/drm/panel/panel-raydium-rm68200.c
+++ b/drivers/gpu/drm/panel/panel-raydium-rm68200.c
@@ -6,9 +6,9 @@
* Yannick Fertre <yannick.fertre@st.com>
*/
-#include <linux/backlight.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/regulator/consumer.h>
@@ -78,7 +78,6 @@ struct rm68200 {
struct drm_panel panel;
struct gpio_desc *reset_gpio;
struct regulator *supply;
- struct backlight_device *backlight;
bool prepared;
bool enabled;
};
@@ -242,8 +241,6 @@ static int rm68200_disable(struct drm_panel *panel)
if (!ctx->enabled)
return 0;
- backlight_disable(ctx->backlight);
-
ctx->enabled = false;
return 0;
@@ -328,18 +325,17 @@ static int rm68200_enable(struct drm_panel *panel)
if (ctx->enabled)
return 0;
- backlight_enable(ctx->backlight);
-
ctx->enabled = true;
return 0;
}
-static int rm68200_get_modes(struct drm_panel *panel)
+static int rm68200_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
{
struct drm_display_mode *mode;
- mode = drm_mode_duplicate(panel->drm, &default_mode);
+ mode = drm_mode_duplicate(connector->dev, &default_mode);
if (!mode) {
DRM_ERROR("failed to add mode %ux%ux@%u\n",
default_mode.hdisplay, default_mode.vdisplay,
@@ -350,10 +346,10 @@ static int rm68200_get_modes(struct drm_panel *panel)
drm_mode_set_name(mode);
mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
- drm_mode_probed_add(panel->connector, mode);
+ drm_mode_probed_add(connector, mode);
- panel->connector->display_info.width_mm = mode->width_mm;
- panel->connector->display_info.height_mm = mode->height_mm;
+ connector->display_info.width_mm = mode->width_mm;
+ connector->display_info.height_mm = mode->height_mm;
return 1;
}
@@ -391,10 +387,6 @@ static int rm68200_probe(struct mipi_dsi_device *dsi)
return ret;
}
- ctx->backlight = devm_of_find_backlight(dev);
- if (IS_ERR(ctx->backlight))
- return PTR_ERR(ctx->backlight);
-
mipi_dsi_set_drvdata(dsi, ctx);
ctx->dev = dev;
@@ -404,9 +396,12 @@ static int rm68200_probe(struct mipi_dsi_device *dsi)
dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
MIPI_DSI_MODE_LPM;
- drm_panel_init(&ctx->panel);
- ctx->panel.dev = dev;
- ctx->panel.funcs = &rm68200_drm_funcs;
+ drm_panel_init(&ctx->panel, dev, &rm68200_drm_funcs,
+ DRM_MODE_CONNECTOR_DSI);
+
+ ret = drm_panel_of_backlight(&ctx->panel);
+ if (ret)
+ return ret;
drm_panel_add(&ctx->panel);
diff --git a/drivers/gpu/drm/panel/panel-rocktech-jh057n00900.c b/drivers/gpu/drm/panel/panel-rocktech-jh057n00900.c
index 6dcb692c4701..38ff742bc120 100644
--- a/drivers/gpu/drm/panel/panel-rocktech-jh057n00900.c
+++ b/drivers/gpu/drm/panel/panel-rocktech-jh057n00900.c
@@ -5,19 +5,22 @@
* Copyright (C) Purism SPC 2019
*/
-#include <drm/drm_mipi_dsi.h>
-#include <drm/drm_modes.h>
-#include <drm/drm_panel.h>
-#include <drm/drm_print.h>
-#include <linux/backlight.h>
#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 */
@@ -33,6 +36,7 @@
#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
@@ -45,7 +49,8 @@ struct jh057n {
struct device *dev;
struct drm_panel panel;
struct gpio_desc *reset_gpio;
- struct backlight_device *backlight;
+ struct regulator *vcc;
+ struct regulator *iovcc;
bool prepared;
struct dentry *debugfs;
@@ -94,7 +99,7 @@ static int jh057n_init_sequence(struct jh057n *ctx)
msleep(20);
dsi_generic_write_seq(dsi, ST7703_CMD_SETVCOM, 0x3F, 0x3F);
- dsi_generic_write_seq(dsi, 0xBF, 0x02, 0x11, 0x00);
+ 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,
@@ -123,7 +128,7 @@ static int jh057n_init_sequence(struct jh057n *ctx)
ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
if (ret < 0) {
- DRM_DEV_ERROR(dev, "Failed to exit sleep mode\n");
+ DRM_DEV_ERROR(dev, "Failed to exit sleep mode: %d\n", ret);
return ret;
}
/* Panel is operational 120 msec after reset */
@@ -139,26 +144,35 @@ static int jh057n_init_sequence(struct jh057n *ctx)
static int jh057n_enable(struct drm_panel *panel)
{
struct jh057n *ctx = panel_to_jh057n(panel);
+ int ret;
- return backlight_enable(ctx->backlight);
+ 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 backlight_disable(ctx->backlight);
+ return mipi_dsi_dcs_set_display_off(dsi);
}
static int jh057n_unprepare(struct drm_panel *panel)
{
struct jh057n *ctx = panel_to_jh057n(panel);
- struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
if (!ctx->prepared)
return 0;
- mipi_dsi_dcs_set_display_off(dsi);
+ regulator_disable(ctx->iovcc);
+ regulator_disable(ctx->vcc);
ctx->prepared = false;
return 0;
@@ -173,21 +187,31 @@ static int jh057n_prepare(struct drm_panel *panel)
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);
- ret = jh057n_init_sequence(ctx);
- if (ret < 0) {
- DRM_DEV_ERROR(ctx->dev, "Panel init sequence failed: %d\n",
- ret);
- return ret;
- }
-
ctx->prepared = true;
return 0;
+
+disable_vcc:
+ regulator_disable(ctx->vcc);
+ return ret;
}
static const struct drm_display_mode default_mode = {
@@ -206,12 +230,13 @@ static const struct drm_display_mode default_mode = {
.height_mm = 130,
};
-static int jh057n_get_modes(struct drm_panel *panel)
+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(panel->drm, &default_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,
@@ -222,9 +247,9 @@ static int jh057n_get_modes(struct drm_panel *panel)
drm_mode_set_name(mode);
mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
- panel->connector->display_info.width_mm = mode->width_mm;
- panel->connector->display_info.height_mm = mode->height_mm;
- drm_mode_probed_add(panel->connector, mode);
+ connector->display_info.width_mm = mode->width_mm;
+ connector->display_info.height_mm = mode->height_mm;
+ drm_mode_probed_add(connector, mode);
return 1;
}
@@ -296,19 +321,39 @@ static int jh057n_probe(struct mipi_dsi_device *dsi)
dsi->mode_flags = MIPI_DSI_MODE_VIDEO |
MIPI_DSI_MODE_VIDEO_BURST | MIPI_DSI_MODE_VIDEO_SYNC_PULSE;
- ctx->backlight = devm_of_find_backlight(dev);
- if (IS_ERR(ctx->backlight))
- return PTR_ERR(ctx->backlight);
+ 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);
- drm_panel_init(&ctx->panel);
- ctx->panel.dev = dev;
- ctx->panel.funcs = &jh057n_drm_funcs;
+ 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. Is host ready?\n");
+ DRM_DEV_ERROR(dev,
+ "mipi_dsi_attach failed (%d). Is host ready?\n",
+ ret);
drm_panel_remove(&ctx->panel);
return ret;
}
@@ -327,12 +372,12 @@ static void jh057n_shutdown(struct mipi_dsi_device *dsi)
struct jh057n *ctx = mipi_dsi_get_drvdata(dsi);
int ret;
- ret = jh057n_unprepare(&ctx->panel);
+ ret = drm_panel_unprepare(&ctx->panel);
if (ret < 0)
DRM_DEV_ERROR(&dsi->dev, "Failed to unprepare panel: %d\n",
ret);
- ret = jh057n_disable(&ctx->panel);
+ ret = drm_panel_disable(&ctx->panel);
if (ret < 0)
DRM_DEV_ERROR(&dsi->dev, "Failed to disable panel: %d\n",
ret);
diff --git a/drivers/gpu/drm/panel/panel-ronbo-rb070d30.c b/drivers/gpu/drm/panel/panel-ronbo-rb070d30.c
index 3c15764f0c03..ef18559e237e 100644
--- a/drivers/gpu/drm/panel/panel-ronbo-rb070d30.c
+++ b/drivers/gpu/drm/panel/panel-ronbo-rb070d30.c
@@ -7,7 +7,6 @@
* This file based on panel-ilitek-ili9881c.c
*/
-#include <linux/backlight.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/err.h>
@@ -29,7 +28,6 @@
struct rb070d30_panel {
struct drm_panel panel;
struct mipi_dsi_device *dsi;
- struct backlight_device *backlight;
struct regulator *supply;
struct {
@@ -84,22 +82,13 @@ static int rb070d30_panel_enable(struct drm_panel *panel)
if (ret)
return ret;
- ret = backlight_enable(ctx->backlight);
- if (ret)
- goto out;
-
return 0;
-
-out:
- mipi_dsi_dcs_enter_sleep_mode(ctx->dsi);
- return ret;
}
static int rb070d30_panel_disable(struct drm_panel *panel)
{
struct rb070d30_panel *ctx = panel_to_rb070d30_panel(panel);
- backlight_disable(ctx->backlight);
return mipi_dsi_dcs_enter_sleep_mode(ctx->dsi);
}
@@ -120,14 +109,14 @@ static const struct drm_display_mode default_mode = {
.height_mm = 85,
};
-static int rb070d30_panel_get_modes(struct drm_panel *panel)
+static int rb070d30_panel_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
{
- struct drm_connector *connector = panel->connector;
struct rb070d30_panel *ctx = panel_to_rb070d30_panel(panel);
struct drm_display_mode *mode;
static const u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
- mode = drm_mode_duplicate(panel->drm, &default_mode);
+ mode = drm_mode_duplicate(connector->dev, &default_mode);
if (!mode) {
DRM_DEV_ERROR(&ctx->dsi->dev,
"Failed to add mode " DRM_MODE_FMT "\n",
@@ -140,9 +129,9 @@ static int rb070d30_panel_get_modes(struct drm_panel *panel)
mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
drm_mode_probed_add(connector, mode);
- panel->connector->display_info.bpc = 8;
- panel->connector->display_info.width_mm = mode->width_mm;
- panel->connector->display_info.height_mm = mode->height_mm;
+ connector->display_info.bpc = 8;
+ connector->display_info.width_mm = mode->width_mm;
+ connector->display_info.height_mm = mode->height_mm;
drm_display_info_set_bus_formats(&connector->display_info,
&bus_format, 1);
@@ -173,9 +162,8 @@ static int rb070d30_panel_dsi_probe(struct mipi_dsi_device *dsi)
mipi_dsi_set_drvdata(dsi, ctx);
ctx->dsi = dsi;
- drm_panel_init(&ctx->panel);
- ctx->panel.dev = &dsi->dev;
- ctx->panel.funcs = &rb070d30_panel_funcs;
+ drm_panel_init(&ctx->panel, &dsi->dev, &rb070d30_panel_funcs,
+ DRM_MODE_CONNECTOR_DSI);
ctx->gpios.reset = devm_gpiod_get(&dsi->dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR(ctx->gpios.reset)) {
@@ -209,11 +197,9 @@ static int rb070d30_panel_dsi_probe(struct mipi_dsi_device *dsi)
return PTR_ERR(ctx->gpios.shlr);
}
- ctx->backlight = devm_of_find_backlight(&dsi->dev);
- if (IS_ERR(ctx->backlight)) {
- DRM_DEV_ERROR(&dsi->dev, "Couldn't get our backlight\n");
- return PTR_ERR(ctx->backlight);
- }
+ ret = drm_panel_of_backlight(&ctx->panel);
+ if (ret)
+ return ret;
ret = drm_panel_add(&ctx->panel);
if (ret < 0)
diff --git a/drivers/gpu/drm/panel/panel-samsung-ld9040.c b/drivers/gpu/drm/panel/panel-samsung-ld9040.c
index 3be902dcedc0..3c52f15f7a1c 100644
--- a/drivers/gpu/drm/panel/panel-samsung-ld9040.c
+++ b/drivers/gpu/drm/panel/panel-samsung-ld9040.c
@@ -261,9 +261,9 @@ static int ld9040_enable(struct drm_panel *panel)
return 0;
}
-static int ld9040_get_modes(struct drm_panel *panel)
+static int ld9040_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
{
- struct drm_connector *connector = panel->connector;
struct ld9040 *ctx = panel_to_ld9040(panel);
struct drm_display_mode *mode;
@@ -351,9 +351,8 @@ static int ld9040_probe(struct spi_device *spi)
return ret;
}
- drm_panel_init(&ctx->panel);
- ctx->panel.dev = dev;
- ctx->panel.funcs = &ld9040_drm_funcs;
+ drm_panel_init(&ctx->panel, dev, &ld9040_drm_funcs,
+ DRM_MODE_CONNECTOR_DPI);
return drm_panel_add(&ctx->panel);
}
diff --git a/drivers/gpu/drm/panel/panel-samsung-s6d16d0.c b/drivers/gpu/drm/panel/panel-samsung-s6d16d0.c
index f75bef24e050..2150043dcf6b 100644
--- a/drivers/gpu/drm/panel/panel-samsung-s6d16d0.c
+++ b/drivers/gpu/drm/panel/panel-samsung-s6d16d0.c
@@ -143,12 +143,12 @@ static int s6d16d0_disable(struct drm_panel *panel)
return 0;
}
-static int s6d16d0_get_modes(struct drm_panel *panel)
+static int s6d16d0_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
{
- struct drm_connector *connector = panel->connector;
struct drm_display_mode *mode;
- mode = drm_mode_duplicate(panel->drm, &samsung_s6d16d0_mode);
+ mode = drm_mode_duplicate(connector->dev, &samsung_s6d16d0_mode);
if (!mode) {
DRM_ERROR("bad mode or failed to add mode\n");
return -EINVAL;
@@ -215,9 +215,8 @@ static int s6d16d0_probe(struct mipi_dsi_device *dsi)
return ret;
}
- drm_panel_init(&s6->panel);
- s6->panel.dev = dev;
- s6->panel.funcs = &s6d16d0_drm_funcs;
+ drm_panel_init(&s6->panel, dev, &s6d16d0_drm_funcs,
+ DRM_MODE_CONNECTOR_DSI);
ret = drm_panel_add(&s6->panel);
if (ret < 0)
diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c b/drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c
index b923de23ed65..36ebd5a4ac7b 100644
--- a/drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c
+++ b/drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c
@@ -645,13 +645,13 @@ static const struct s6e3ha2_panel_desc samsung_s6e3hf2 = {
.type = HF2_TYPE,
};
-static int s6e3ha2_get_modes(struct drm_panel *panel)
+static int s6e3ha2_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
{
- struct drm_connector *connector = panel->connector;
struct s6e3ha2 *ctx = container_of(panel, struct s6e3ha2, panel);
struct drm_display_mode *mode;
- mode = drm_mode_duplicate(panel->drm, ctx->desc->mode);
+ mode = drm_mode_duplicate(connector->dev, ctx->desc->mode);
if (!mode) {
DRM_ERROR("failed to add mode %ux%ux@%u\n",
ctx->desc->mode->hdisplay, ctx->desc->mode->vdisplay,
@@ -732,9 +732,8 @@ static int s6e3ha2_probe(struct mipi_dsi_device *dsi)
ctx->bl_dev->props.brightness = S6E3HA2_DEFAULT_BRIGHTNESS;
ctx->bl_dev->props.power = FB_BLANK_POWERDOWN;
- drm_panel_init(&ctx->panel);
- ctx->panel.dev = dev;
- ctx->panel.funcs = &s6e3ha2_drm_funcs;
+ drm_panel_init(&ctx->panel, dev, &s6e3ha2_drm_funcs,
+ DRM_MODE_CONNECTOR_DSI);
ret = drm_panel_add(&ctx->panel);
if (ret < 0)
diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e63j0x03.c b/drivers/gpu/drm/panel/panel-samsung-s6e63j0x03.c
index cd90fa700c49..a3570e0a90a8 100644
--- a/drivers/gpu/drm/panel/panel-samsung-s6e63j0x03.c
+++ b/drivers/gpu/drm/panel/panel-samsung-s6e63j0x03.c
@@ -400,12 +400,12 @@ static int s6e63j0x03_enable(struct drm_panel *panel)
return 0;
}
-static int s6e63j0x03_get_modes(struct drm_panel *panel)
+static int s6e63j0x03_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
{
- struct drm_connector *connector = panel->connector;
struct drm_display_mode *mode;
- mode = drm_mode_duplicate(panel->drm, &default_mode);
+ mode = drm_mode_duplicate(connector->dev, &default_mode);
if (!mode) {
DRM_ERROR("failed to add mode %ux%ux@%u\n",
default_mode.hdisplay, default_mode.vdisplay,
@@ -466,9 +466,8 @@ static int s6e63j0x03_probe(struct mipi_dsi_device *dsi)
return PTR_ERR(ctx->reset_gpio);
}
- drm_panel_init(&ctx->panel);
- ctx->panel.dev = dev;
- ctx->panel.funcs = &s6e63j0x03_funcs;
+ drm_panel_init(&ctx->panel, dev, &s6e63j0x03_funcs,
+ DRM_MODE_CONNECTOR_DSI);
ctx->bl_dev = backlight_device_register("s6e63j0x03", dev, ctx,
&s6e63j0x03_bl_ops, NULL);
diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e63m0.c b/drivers/gpu/drm/panel/panel-samsung-s6e63m0.c
index 142d395ea512..a5f76eb4fa25 100644
--- a/drivers/gpu/drm/panel/panel-samsung-s6e63m0.c
+++ b/drivers/gpu/drm/panel/panel-samsung-s6e63m0.c
@@ -362,12 +362,12 @@ static int s6e63m0_enable(struct drm_panel *panel)
return 0;
}
-static int s6e63m0_get_modes(struct drm_panel *panel)
+static int s6e63m0_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
{
- struct drm_connector *connector = panel->connector;
struct drm_display_mode *mode;
- mode = drm_mode_duplicate(panel->drm, &default_mode);
+ mode = drm_mode_duplicate(connector->dev, &default_mode);
if (!mode) {
DRM_ERROR("failed to add mode %ux%ux@%u\n",
default_mode.hdisplay, default_mode.vdisplay,
@@ -473,9 +473,8 @@ static int s6e63m0_probe(struct spi_device *spi)
return ret;
}
- drm_panel_init(&ctx->panel);
- ctx->panel.dev = dev;
- ctx->panel.funcs = &s6e63m0_drm_funcs;
+ drm_panel_init(&ctx->panel, dev, &s6e63m0_drm_funcs,
+ DRM_MODE_CONNECTOR_DPI);
ret = s6e63m0_backlight_register(ctx);
if (ret < 0)
diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e8aa0.c b/drivers/gpu/drm/panel/panel-samsung-s6e8aa0.c
index 81858267723a..8a028d2bd0d6 100644
--- a/drivers/gpu/drm/panel/panel-samsung-s6e8aa0.c
+++ b/drivers/gpu/drm/panel/panel-samsung-s6e8aa0.c
@@ -920,9 +920,9 @@ static int s6e8aa0_enable(struct drm_panel *panel)
return 0;
}
-static int s6e8aa0_get_modes(struct drm_panel *panel)
+static int s6e8aa0_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
{
- struct drm_connector *connector = panel->connector;
struct s6e8aa0 *ctx = panel_to_s6e8aa0(panel);
struct drm_display_mode *mode;
@@ -1017,9 +1017,8 @@ static int s6e8aa0_probe(struct mipi_dsi_device *dsi)
ctx->brightness = GAMMA_LEVEL_NUM - 1;
- drm_panel_init(&ctx->panel);
- ctx->panel.dev = dev;
- ctx->panel.funcs = &s6e8aa0_drm_funcs;
+ drm_panel_init(&ctx->panel, dev, &s6e8aa0_drm_funcs,
+ DRM_MODE_CONNECTOR_DSI);
ret = drm_panel_add(&ctx->panel);
if (ret < 0)
diff --git a/drivers/gpu/drm/panel/panel-seiko-43wvf1g.c b/drivers/gpu/drm/panel/panel-seiko-43wvf1g.c
index 18b22b1294fb..40fcbbbacb2c 100644
--- a/drivers/gpu/drm/panel/panel-seiko-43wvf1g.c
+++ b/drivers/gpu/drm/panel/panel-seiko-43wvf1g.c
@@ -6,7 +6,6 @@
* Based on Panel Simple driver by Thierry Reding <treding@nvidia.com>
*/
-#include <linux/backlight.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/of.h>
@@ -46,7 +45,6 @@ struct seiko_panel {
bool prepared;
bool enabled;
const struct seiko_panel_desc *desc;
- struct backlight_device *backlight;
struct regulator *dvdd;
struct regulator *avdd;
};
@@ -56,10 +54,9 @@ static inline struct seiko_panel *to_seiko_panel(struct drm_panel *panel)
return container_of(panel, struct seiko_panel, base);
}
-static int seiko_panel_get_fixed_modes(struct seiko_panel *panel)
+static int seiko_panel_get_fixed_modes(struct seiko_panel *panel,
+ struct drm_connector *connector)
{
- struct drm_connector *connector = panel->base.connector;
- struct drm_device *drm = panel->base.drm;
struct drm_display_mode *mode;
unsigned int i, num = 0;
@@ -71,9 +68,9 @@ static int seiko_panel_get_fixed_modes(struct seiko_panel *panel)
struct videomode vm;
videomode_from_timing(dt, &vm);
- mode = drm_mode_create(drm);
+ mode = drm_mode_create(connector->dev);
if (!mode) {
- dev_err(drm->dev, "failed to add mode %ux%u\n",
+ dev_err(panel->base.dev, "failed to add mode %ux%u\n",
dt->hactive.typ, dt->vactive.typ);
continue;
}
@@ -92,9 +89,9 @@ static int seiko_panel_get_fixed_modes(struct seiko_panel *panel)
for (i = 0; i < panel->desc->num_modes; i++) {
const struct drm_display_mode *m = &panel->desc->modes[i];
- mode = drm_mode_duplicate(drm, m);
+ mode = drm_mode_duplicate(connector->dev, m);
if (!mode) {
- dev_err(drm->dev, "failed to add mode %ux%u@%u\n",
+ dev_err(panel->base.dev, "failed to add mode %ux%u@%u\n",
m->hdisplay, m->vdisplay, m->vrefresh);
continue;
}
@@ -128,12 +125,6 @@ static int seiko_panel_disable(struct drm_panel *panel)
if (!p->enabled)
return 0;
- if (p->backlight) {
- p->backlight->props.power = FB_BLANK_POWERDOWN;
- p->backlight->props.state |= BL_CORE_FBBLANK;
- backlight_update_status(p->backlight);
- }
-
p->enabled = false;
return 0;
@@ -197,23 +188,18 @@ static int seiko_panel_enable(struct drm_panel *panel)
if (p->enabled)
return 0;
- if (p->backlight) {
- p->backlight->props.state &= ~BL_CORE_FBBLANK;
- p->backlight->props.power = FB_BLANK_UNBLANK;
- backlight_update_status(p->backlight);
- }
-
p->enabled = true;
return 0;
}
-static int seiko_panel_get_modes(struct drm_panel *panel)
+static int seiko_panel_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
{
struct seiko_panel *p = to_seiko_panel(panel);
/* add hard-coded panel modes */
- return seiko_panel_get_fixed_modes(p);
+ return seiko_panel_get_fixed_modes(p, connector);
}
static int seiko_panel_get_timings(struct drm_panel *panel,
@@ -245,7 +231,6 @@ static const struct drm_panel_funcs seiko_panel_funcs = {
static int seiko_panel_probe(struct device *dev,
const struct seiko_panel_desc *desc)
{
- struct device_node *backlight;
struct seiko_panel *panel;
int err;
@@ -265,18 +250,12 @@ static int seiko_panel_probe(struct device *dev,
if (IS_ERR(panel->avdd))
return PTR_ERR(panel->avdd);
- backlight = of_parse_phandle(dev->of_node, "backlight", 0);
- if (backlight) {
- panel->backlight = of_find_backlight_by_node(backlight);
- of_node_put(backlight);
+ drm_panel_init(&panel->base, dev, &seiko_panel_funcs,
+ DRM_MODE_CONNECTOR_DPI);
- if (!panel->backlight)
- return -EPROBE_DEFER;
- }
-
- drm_panel_init(&panel->base);
- panel->base.dev = dev;
- panel->base.funcs = &seiko_panel_funcs;
+ err = drm_panel_of_backlight(&panel->base);
+ if (err)
+ return err;
err = drm_panel_add(&panel->base);
if (err < 0)
@@ -292,11 +271,7 @@ static int seiko_panel_remove(struct platform_device *pdev)
struct seiko_panel *panel = dev_get_drvdata(&pdev->dev);
drm_panel_remove(&panel->base);
-
- seiko_panel_disable(&panel->base);
-
- if (panel->backlight)
- put_device(&panel->backlight->dev);
+ drm_panel_disable(&panel->base);
return 0;
}
@@ -305,7 +280,7 @@ static void seiko_panel_shutdown(struct platform_device *pdev)
{
struct seiko_panel *panel = dev_get_drvdata(&pdev->dev);
- seiko_panel_disable(&panel->base);
+ drm_panel_disable(&panel->base);
}
static const struct display_timing seiko_43wvf1g_timing = {
diff --git a/drivers/gpu/drm/panel/panel-sharp-lq101r1sx01.c b/drivers/gpu/drm/panel/panel-sharp-lq101r1sx01.c
index e910b4ad1310..b5d1977221a7 100644
--- a/drivers/gpu/drm/panel/panel-sharp-lq101r1sx01.c
+++ b/drivers/gpu/drm/panel/panel-sharp-lq101r1sx01.c
@@ -3,7 +3,6 @@
* Copyright (C) 2014 NVIDIA Corporation
*/
-#include <linux/backlight.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/module.h>
@@ -23,7 +22,6 @@ struct sharp_panel {
struct mipi_dsi_device *link1;
struct mipi_dsi_device *link2;
- struct backlight_device *backlight;
struct regulator *supply;
bool prepared;
@@ -94,8 +92,6 @@ static int sharp_panel_disable(struct drm_panel *panel)
if (!sharp->enabled)
return 0;
- backlight_disable(sharp->backlight);
-
sharp->enabled = false;
return 0;
@@ -258,8 +254,6 @@ static int sharp_panel_enable(struct drm_panel *panel)
if (sharp->enabled)
return 0;
- backlight_enable(sharp->backlight);
-
sharp->enabled = true;
return 0;
@@ -278,13 +272,14 @@ static const struct drm_display_mode default_mode = {
.vrefresh = 60,
};
-static int sharp_panel_get_modes(struct drm_panel *panel)
+static int sharp_panel_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
{
struct drm_display_mode *mode;
- mode = drm_mode_duplicate(panel->drm, &default_mode);
+ mode = drm_mode_duplicate(connector->dev, &default_mode);
if (!mode) {
- dev_err(panel->drm->dev, "failed to add mode %ux%ux@%u\n",
+ dev_err(panel->dev, "failed to add mode %ux%ux@%u\n",
default_mode.hdisplay, default_mode.vdisplay,
default_mode.vrefresh);
return -ENOMEM;
@@ -292,10 +287,10 @@ static int sharp_panel_get_modes(struct drm_panel *panel)
drm_mode_set_name(mode);
- drm_mode_probed_add(panel->connector, mode);
+ drm_mode_probed_add(connector, mode);
- panel->connector->display_info.width_mm = 217;
- panel->connector->display_info.height_mm = 136;
+ connector->display_info.width_mm = 217;
+ connector->display_info.height_mm = 136;
return 1;
}
@@ -316,7 +311,7 @@ MODULE_DEVICE_TABLE(of, sharp_of_match);
static int sharp_panel_add(struct sharp_panel *sharp)
{
- struct device *dev = &sharp->link1->dev;
+ int ret;
sharp->mode = &default_mode;
@@ -324,14 +319,12 @@ static int sharp_panel_add(struct sharp_panel *sharp)
if (IS_ERR(sharp->supply))
return PTR_ERR(sharp->supply);
- sharp->backlight = devm_of_find_backlight(dev);
-
- if (IS_ERR(sharp->backlight))
- return PTR_ERR(sharp->backlight);
+ drm_panel_init(&sharp->base, &sharp->link1->dev, &sharp_panel_funcs,
+ DRM_MODE_CONNECTOR_DSI);
- drm_panel_init(&sharp->base);
- sharp->base.funcs = &sharp_panel_funcs;
- sharp->base.dev = &sharp->link1->dev;
+ ret = drm_panel_of_backlight(&sharp->base);
+ if (ret)
+ return ret;
return drm_panel_add(&sharp->base);
}
@@ -408,7 +401,7 @@ static int sharp_panel_remove(struct mipi_dsi_device *dsi)
return 0;
}
- err = sharp_panel_disable(&sharp->base);
+ err = drm_panel_disable(&sharp->base);
if (err < 0)
dev_err(&dsi->dev, "failed to disable panel: %d\n", err);
@@ -429,7 +422,7 @@ static void sharp_panel_shutdown(struct mipi_dsi_device *dsi)
if (!sharp)
return;
- sharp_panel_disable(&sharp->base);
+ drm_panel_disable(&sharp->base);
}
static struct mipi_dsi_driver sharp_panel_driver = {
diff --git a/drivers/gpu/drm/panel/panel-sharp-ls037v7dw01.c b/drivers/gpu/drm/panel/panel-sharp-ls037v7dw01.c
new file mode 100644
index 000000000000..1cf3f02435c1
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-sharp-ls037v7dw01.c
@@ -0,0 +1,225 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Sharp LS037V7DW01 LCD Panel Driver
+ *
+ * Copyright (C) 2019 Texas Instruments Incorporated
+ *
+ * Based on the omapdrm-specific panel-sharp-ls037v7dw01 driver
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated
+ * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+
+#include <drm/drm_connector.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_panel.h>
+
+struct ls037v7dw01_panel {
+ struct drm_panel panel;
+ struct platform_device *pdev;
+
+ struct regulator *vdd;
+ struct gpio_desc *resb_gpio; /* low = reset active min 20 us */
+ struct gpio_desc *ini_gpio; /* high = power on */
+ struct gpio_desc *mo_gpio; /* low = 480x640, high = 240x320 */
+ struct gpio_desc *lr_gpio; /* high = conventional horizontal scanning */
+ struct gpio_desc *ud_gpio; /* high = conventional vertical scanning */
+};
+
+#define to_ls037v7dw01_device(p) \
+ container_of(p, struct ls037v7dw01_panel, panel)
+
+static int ls037v7dw01_disable(struct drm_panel *panel)
+{
+ struct ls037v7dw01_panel *lcd = to_ls037v7dw01_device(panel);
+
+ gpiod_set_value_cansleep(lcd->ini_gpio, 0);
+ gpiod_set_value_cansleep(lcd->resb_gpio, 0);
+
+ /* Wait at least 5 vsyncs after disabling the LCD. */
+ msleep(100);
+
+ return 0;
+}
+
+static int ls037v7dw01_unprepare(struct drm_panel *panel)
+{
+ struct ls037v7dw01_panel *lcd = to_ls037v7dw01_device(panel);
+
+ regulator_disable(lcd->vdd);
+ return 0;
+}
+
+static int ls037v7dw01_prepare(struct drm_panel *panel)
+{
+ struct ls037v7dw01_panel *lcd = to_ls037v7dw01_device(panel);
+ int ret;
+
+ ret = regulator_enable(lcd->vdd);
+ if (ret < 0)
+ dev_err(&lcd->pdev->dev, "%s: failed to enable regulator\n",
+ __func__);
+
+ return ret;
+}
+
+static int ls037v7dw01_enable(struct drm_panel *panel)
+{
+ struct ls037v7dw01_panel *lcd = to_ls037v7dw01_device(panel);
+
+ /* Wait couple of vsyncs before enabling the LCD. */
+ msleep(50);
+
+ gpiod_set_value_cansleep(lcd->resb_gpio, 1);
+ gpiod_set_value_cansleep(lcd->ini_gpio, 1);
+
+ return 0;
+}
+
+static const struct drm_display_mode ls037v7dw01_mode = {
+ .clock = 19200,
+ .hdisplay = 480,
+ .hsync_start = 480 + 1,
+ .hsync_end = 480 + 1 + 2,
+ .htotal = 480 + 1 + 2 + 28,
+ .vdisplay = 640,
+ .vsync_start = 640 + 1,
+ .vsync_end = 640 + 1 + 1,
+ .vtotal = 640 + 1 + 1 + 1,
+ .vrefresh = 58,
+ .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
+ .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
+ .width_mm = 56,
+ .height_mm = 75,
+};
+
+static int ls037v7dw01_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
+{
+ struct drm_display_mode *mode;
+
+ mode = drm_mode_duplicate(connector->dev, &ls037v7dw01_mode);
+ if (!mode)
+ return -ENOMEM;
+
+ drm_mode_set_name(mode);
+ drm_mode_probed_add(connector, mode);
+
+ connector->display_info.width_mm = ls037v7dw01_mode.width_mm;
+ connector->display_info.height_mm = ls037v7dw01_mode.height_mm;
+ /*
+ * FIXME: According to the datasheet pixel data is sampled on the
+ * rising edge of the clock, but the code running on the SDP3430
+ * indicates sampling on the negative edge. This should be tested on a
+ * real device.
+ */
+ connector->display_info.bus_flags = DRM_BUS_FLAG_DE_HIGH
+ | DRM_BUS_FLAG_SYNC_SAMPLE_POSEDGE
+ | DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE;
+
+ return 1;
+}
+
+static const struct drm_panel_funcs ls037v7dw01_funcs = {
+ .disable = ls037v7dw01_disable,
+ .unprepare = ls037v7dw01_unprepare,
+ .prepare = ls037v7dw01_prepare,
+ .enable = ls037v7dw01_enable,
+ .get_modes = ls037v7dw01_get_modes,
+};
+
+static int ls037v7dw01_probe(struct platform_device *pdev)
+{
+ struct ls037v7dw01_panel *lcd;
+
+ lcd = devm_kzalloc(&pdev->dev, sizeof(*lcd), GFP_KERNEL);
+ if (!lcd)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, lcd);
+ lcd->pdev = pdev;
+
+ lcd->vdd = devm_regulator_get(&pdev->dev, "envdd");
+ if (IS_ERR(lcd->vdd)) {
+ dev_err(&pdev->dev, "failed to get regulator\n");
+ return PTR_ERR(lcd->vdd);
+ }
+
+ lcd->ini_gpio = devm_gpiod_get(&pdev->dev, "enable", GPIOD_OUT_LOW);
+ if (IS_ERR(lcd->ini_gpio)) {
+ dev_err(&pdev->dev, "failed to get enable gpio\n");
+ return PTR_ERR(lcd->ini_gpio);
+ }
+
+ lcd->resb_gpio = devm_gpiod_get(&pdev->dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(lcd->resb_gpio)) {
+ dev_err(&pdev->dev, "failed to get reset gpio\n");
+ return PTR_ERR(lcd->resb_gpio);
+ }
+
+ lcd->mo_gpio = devm_gpiod_get_index(&pdev->dev, "mode", 0,
+ GPIOD_OUT_LOW);
+ if (IS_ERR(lcd->mo_gpio)) {
+ dev_err(&pdev->dev, "failed to get mode[0] gpio\n");
+ return PTR_ERR(lcd->mo_gpio);
+ }
+
+ lcd->lr_gpio = devm_gpiod_get_index(&pdev->dev, "mode", 1,
+ GPIOD_OUT_LOW);
+ if (IS_ERR(lcd->lr_gpio)) {
+ dev_err(&pdev->dev, "failed to get mode[1] gpio\n");
+ return PTR_ERR(lcd->lr_gpio);
+ }
+
+ lcd->ud_gpio = devm_gpiod_get_index(&pdev->dev, "mode", 2,
+ GPIOD_OUT_LOW);
+ if (IS_ERR(lcd->ud_gpio)) {
+ dev_err(&pdev->dev, "failed to get mode[2] gpio\n");
+ return PTR_ERR(lcd->ud_gpio);
+ }
+
+ drm_panel_init(&lcd->panel, &pdev->dev, &ls037v7dw01_funcs,
+ DRM_MODE_CONNECTOR_DPI);
+
+ return drm_panel_add(&lcd->panel);
+}
+
+static int ls037v7dw01_remove(struct platform_device *pdev)
+{
+ struct ls037v7dw01_panel *lcd = platform_get_drvdata(pdev);
+
+ drm_panel_remove(&lcd->panel);
+ drm_panel_disable(&lcd->panel);
+ drm_panel_unprepare(&lcd->panel);
+
+ return 0;
+}
+
+static const struct of_device_id ls037v7dw01_of_match[] = {
+ { .compatible = "sharp,ls037v7dw01", },
+ { /* sentinel */ },
+};
+
+MODULE_DEVICE_TABLE(of, ls037v7dw01_of_match);
+
+static struct platform_driver ls037v7dw01_driver = {
+ .probe = ls037v7dw01_probe,
+ .remove = ls037v7dw01_remove,
+ .driver = {
+ .name = "panel-sharp-ls037v7dw01",
+ .of_match_table = ls037v7dw01_of_match,
+ },
+};
+
+module_platform_driver(ls037v7dw01_driver);
+
+MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
+MODULE_DESCRIPTION("Sharp LS037V7DW01 Panel Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/panel/panel-sharp-ls043t1le01.c b/drivers/gpu/drm/panel/panel-sharp-ls043t1le01.c
index c39abde9f9f1..ce586c6d70c7 100644
--- a/drivers/gpu/drm/panel/panel-sharp-ls043t1le01.c
+++ b/drivers/gpu/drm/panel/panel-sharp-ls043t1le01.c
@@ -7,7 +7,6 @@
* Based on AUO panel driver by Rob Clark <robdclark@gmail.com>
*/
-#include <linux/backlight.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/module.h>
@@ -25,7 +24,6 @@ struct sharp_nt_panel {
struct drm_panel base;
struct mipi_dsi_device *dsi;
- struct backlight_device *backlight;
struct regulator *supply;
struct gpio_desc *reset_gpio;
@@ -107,8 +105,6 @@ static int sharp_nt_panel_disable(struct drm_panel *panel)
if (!sharp_nt->enabled)
return 0;
- backlight_disable(sharp_nt->backlight);
-
sharp_nt->enabled = false;
return 0;
@@ -190,8 +186,6 @@ static int sharp_nt_panel_enable(struct drm_panel *panel)
if (sharp_nt->enabled)
return 0;
- backlight_enable(sharp_nt->backlight);
-
sharp_nt->enabled = true;
return 0;
@@ -210,24 +204,25 @@ static const struct drm_display_mode default_mode = {
.vrefresh = 60,
};
-static int sharp_nt_panel_get_modes(struct drm_panel *panel)
+static int sharp_nt_panel_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
{
struct drm_display_mode *mode;
- mode = drm_mode_duplicate(panel->drm, &default_mode);
+ mode = drm_mode_duplicate(connector->dev, &default_mode);
if (!mode) {
- dev_err(panel->drm->dev, "failed to add mode %ux%ux@%u\n",
- default_mode.hdisplay, default_mode.vdisplay,
- default_mode.vrefresh);
+ dev_err(panel->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);
- drm_mode_probed_add(panel->connector, mode);
+ drm_mode_probed_add(connector, mode);
- panel->connector->display_info.width_mm = 54;
- panel->connector->display_info.height_mm = 95;
+ connector->display_info.width_mm = 54;
+ connector->display_info.height_mm = 95;
return 1;
}
@@ -243,6 +238,7 @@ static const struct drm_panel_funcs sharp_nt_panel_funcs = {
static int sharp_nt_panel_add(struct sharp_nt_panel *sharp_nt)
{
struct device *dev = &sharp_nt->dsi->dev;
+ int ret;
sharp_nt->mode = &default_mode;
@@ -259,14 +255,12 @@ static int sharp_nt_panel_add(struct sharp_nt_panel *sharp_nt)
gpiod_set_value(sharp_nt->reset_gpio, 0);
}
- sharp_nt->backlight = devm_of_find_backlight(dev);
+ drm_panel_init(&sharp_nt->base, &sharp_nt->dsi->dev,
+ &sharp_nt_panel_funcs, DRM_MODE_CONNECTOR_DSI);
- if (IS_ERR(sharp_nt->backlight))
- return PTR_ERR(sharp_nt->backlight);
-
- drm_panel_init(&sharp_nt->base);
- sharp_nt->base.funcs = &sharp_nt_panel_funcs;
- sharp_nt->base.dev = &sharp_nt->dsi->dev;
+ ret = drm_panel_of_backlight(&sharp_nt->base);
+ if (ret)
+ return ret;
return drm_panel_add(&sharp_nt->base);
}
@@ -309,7 +303,7 @@ static int sharp_nt_panel_remove(struct mipi_dsi_device *dsi)
struct sharp_nt_panel *sharp_nt = mipi_dsi_get_drvdata(dsi);
int ret;
- ret = sharp_nt_panel_disable(&sharp_nt->base);
+ ret = drm_panel_disable(&sharp_nt->base);
if (ret < 0)
dev_err(&dsi->dev, "failed to disable panel: %d\n", ret);
@@ -326,7 +320,7 @@ static void sharp_nt_panel_shutdown(struct mipi_dsi_device *dsi)
{
struct sharp_nt_panel *sharp_nt = mipi_dsi_get_drvdata(dsi);
- sharp_nt_panel_disable(&sharp_nt->base);
+ drm_panel_disable(&sharp_nt->base);
}
static const struct of_device_id sharp_nt_of_match[] = {
diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c
index 5a93c4edf1e4..e14c14ac62b5 100644
--- a/drivers/gpu/drm/panel/panel-simple.c
+++ b/drivers/gpu/drm/panel/panel-simple.c
@@ -21,7 +21,6 @@
* DEALINGS IN THE SOFTWARE.
*/
-#include <linux/backlight.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/module.h>
@@ -30,6 +29,7 @@
#include <linux/regulator/consumer.h>
#include <video/display_timing.h>
+#include <video/of_display_timing.h>
#include <video/videomode.h>
#include <drm/drm_crtc.h>
@@ -37,6 +37,22 @@
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_panel.h>
+/**
+ * @modes: Pointer to array of fixed modes appropriate for this panel. If
+ * only one mode then this can just be the address of this the mode.
+ * NOTE: cannot be used with "timings" and also if this is specified
+ * then you cannot override the mode in the device tree.
+ * @num_modes: Number of elements in modes array.
+ * @timings: Pointer to array of display timings. NOTE: cannot be used with
+ * "modes" and also these will be used to validate a device tree
+ * override if one is present.
+ * @num_timings: Number of elements in timings array.
+ * @bpc: Bits per color.
+ * @size: Structure containing the physical size of this panel.
+ * @delay: Structure containing various delay values for this panel.
+ * @bus_format: See MEDIA_BUS_FMT_... defines.
+ * @bus_flags: See DRM_BUS_FLAG_... defines.
+ */
struct panel_desc {
const struct drm_display_mode *modes;
unsigned int num_modes;
@@ -77,6 +93,7 @@ struct panel_desc {
u32 bus_format;
u32 bus_flags;
+ int connector_type;
};
struct panel_simple {
@@ -87,11 +104,12 @@ struct panel_simple {
const struct panel_desc *desc;
- struct backlight_device *backlight;
struct regulator *supply;
struct i2c_adapter *ddc;
struct gpio_desc *enable_gpio;
+
+ struct drm_display_mode override_mode;
};
static inline struct panel_simple *to_panel_simple(struct drm_panel *panel)
@@ -99,24 +117,20 @@ static inline struct panel_simple *to_panel_simple(struct drm_panel *panel)
return container_of(panel, struct panel_simple, base);
}
-static int panel_simple_get_fixed_modes(struct panel_simple *panel)
+static unsigned int panel_simple_get_timings_modes(struct panel_simple *panel,
+ struct drm_connector *connector)
{
- struct drm_connector *connector = panel->base.connector;
- struct drm_device *drm = panel->base.drm;
struct drm_display_mode *mode;
unsigned int i, num = 0;
- if (!panel->desc)
- return 0;
-
for (i = 0; i < panel->desc->num_timings; i++) {
const struct display_timing *dt = &panel->desc->timings[i];
struct videomode vm;
videomode_from_timing(dt, &vm);
- mode = drm_mode_create(drm);
+ mode = drm_mode_create(connector->dev);
if (!mode) {
- dev_err(drm->dev, "failed to add mode %ux%u\n",
+ dev_err(panel->base.dev, "failed to add mode %ux%u\n",
dt->hactive.typ, dt->vactive.typ);
continue;
}
@@ -132,12 +146,21 @@ static int panel_simple_get_fixed_modes(struct panel_simple *panel)
num++;
}
+ return num;
+}
+
+static unsigned int panel_simple_get_display_modes(struct panel_simple *panel,
+ struct drm_connector *connector)
+{
+ struct drm_display_mode *mode;
+ unsigned int i, num = 0;
+
for (i = 0; i < panel->desc->num_modes; i++) {
const struct drm_display_mode *m = &panel->desc->modes[i];
- mode = drm_mode_duplicate(drm, m);
+ mode = drm_mode_duplicate(connector->dev, m);
if (!mode) {
- dev_err(drm->dev, "failed to add mode %ux%u@%u\n",
+ dev_err(panel->base.dev, "failed to add mode %ux%u@%u\n",
m->hdisplay, m->vdisplay, m->vrefresh);
continue;
}
@@ -153,6 +176,44 @@ static int panel_simple_get_fixed_modes(struct panel_simple *panel)
num++;
}
+ return num;
+}
+
+static int panel_simple_get_non_edid_modes(struct panel_simple *panel,
+ struct drm_connector *connector)
+{
+ struct drm_display_mode *mode;
+ bool has_override = panel->override_mode.type;
+ unsigned int num = 0;
+
+ if (!panel->desc)
+ return 0;
+
+ if (has_override) {
+ mode = drm_mode_duplicate(connector->dev,
+ &panel->override_mode);
+ if (mode) {
+ drm_mode_probed_add(connector, mode);
+ num = 1;
+ } else {
+ dev_err(panel->base.dev, "failed to add override mode\n");
+ }
+ }
+
+ /* Only add timings if override was not there or failed to validate */
+ if (num == 0 && panel->desc->num_timings)
+ num = panel_simple_get_timings_modes(panel, connector);
+
+ /*
+ * Only add fixed modes if timings/override added no mode.
+ *
+ * We should only ever have either the display timings specified
+ * or a fixed mode. Anything else is rather bogus.
+ */
+ WARN_ON(panel->desc->num_timings && panel->desc->num_modes);
+ if (num == 0)
+ num = panel_simple_get_display_modes(panel, connector);
+
connector->display_info.bpc = panel->desc->bpc;
connector->display_info.width_mm = panel->desc->size.width;
connector->display_info.height_mm = panel->desc->size.height;
@@ -171,12 +232,6 @@ static int panel_simple_disable(struct drm_panel *panel)
if (!p->enabled)
return 0;
- if (p->backlight) {
- p->backlight->props.power = FB_BLANK_POWERDOWN;
- p->backlight->props.state |= BL_CORE_FBBLANK;
- backlight_update_status(p->backlight);
- }
-
if (p->desc->delay.disable)
msleep(p->desc->delay.disable);
@@ -242,34 +297,30 @@ static int panel_simple_enable(struct drm_panel *panel)
if (p->desc->delay.enable)
msleep(p->desc->delay.enable);
- if (p->backlight) {
- p->backlight->props.state &= ~BL_CORE_FBBLANK;
- p->backlight->props.power = FB_BLANK_UNBLANK;
- backlight_update_status(p->backlight);
- }
-
p->enabled = true;
return 0;
}
-static int panel_simple_get_modes(struct drm_panel *panel)
+static int panel_simple_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
{
struct panel_simple *p = to_panel_simple(panel);
int num = 0;
/* probe EDID if a DDC bus is available */
if (p->ddc) {
- struct edid *edid = drm_get_edid(panel->connector, p->ddc);
- drm_connector_update_edid_property(panel->connector, edid);
+ struct edid *edid = drm_get_edid(connector, p->ddc);
+
+ drm_connector_update_edid_property(connector, edid);
if (edid) {
- num += drm_add_edid_modes(panel->connector, edid);
+ num += drm_add_edid_modes(connector, edid);
kfree(edid);
}
}
/* add hard-coded panel modes */
- num += panel_simple_get_fixed_modes(p);
+ num += panel_simple_get_non_edid_modes(p, connector);
return num;
}
@@ -300,10 +351,58 @@ static const struct drm_panel_funcs panel_simple_funcs = {
.get_timings = panel_simple_get_timings,
};
+#define PANEL_SIMPLE_BOUNDS_CHECK(to_check, bounds, field) \
+ (to_check->field.typ >= bounds->field.min && \
+ to_check->field.typ <= bounds->field.max)
+static void panel_simple_parse_panel_timing_node(struct device *dev,
+ struct panel_simple *panel,
+ const struct display_timing *ot)
+{
+ const struct panel_desc *desc = panel->desc;
+ struct videomode vm;
+ unsigned int i;
+
+ if (WARN_ON(desc->num_modes)) {
+ dev_err(dev, "Reject override mode: panel has a fixed mode\n");
+ return;
+ }
+ if (WARN_ON(!desc->num_timings)) {
+ dev_err(dev, "Reject override mode: no timings specified\n");
+ return;
+ }
+
+ for (i = 0; i < panel->desc->num_timings; i++) {
+ const struct display_timing *dt = &panel->desc->timings[i];
+
+ if (!PANEL_SIMPLE_BOUNDS_CHECK(ot, dt, hactive) ||
+ !PANEL_SIMPLE_BOUNDS_CHECK(ot, dt, hfront_porch) ||
+ !PANEL_SIMPLE_BOUNDS_CHECK(ot, dt, hback_porch) ||
+ !PANEL_SIMPLE_BOUNDS_CHECK(ot, dt, hsync_len) ||
+ !PANEL_SIMPLE_BOUNDS_CHECK(ot, dt, vactive) ||
+ !PANEL_SIMPLE_BOUNDS_CHECK(ot, dt, vfront_porch) ||
+ !PANEL_SIMPLE_BOUNDS_CHECK(ot, dt, vback_porch) ||
+ !PANEL_SIMPLE_BOUNDS_CHECK(ot, dt, vsync_len))
+ continue;
+
+ if (ot->flags != dt->flags)
+ continue;
+
+ videomode_from_timing(ot, &vm);
+ drm_display_mode_from_videomode(&vm, &panel->override_mode);
+ panel->override_mode.type |= DRM_MODE_TYPE_DRIVER |
+ DRM_MODE_TYPE_PREFERRED;
+ break;
+ }
+
+ if (WARN_ON(!panel->override_mode.type))
+ dev_err(dev, "Reject override mode: No display_timing found\n");
+}
+
static int panel_simple_probe(struct device *dev, const struct panel_desc *desc)
{
- struct device_node *backlight, *ddc;
struct panel_simple *panel;
+ struct display_timing dt;
+ struct device_node *ddc;
int err;
panel = devm_kzalloc(dev, sizeof(*panel), GFP_KERNEL);
@@ -329,29 +428,24 @@ static int panel_simple_probe(struct device *dev, const struct panel_desc *desc)
return err;
}
- backlight = of_parse_phandle(dev->of_node, "backlight", 0);
- if (backlight) {
- panel->backlight = of_find_backlight_by_node(backlight);
- of_node_put(backlight);
-
- if (!panel->backlight)
- return -EPROBE_DEFER;
- }
-
ddc = of_parse_phandle(dev->of_node, "ddc-i2c-bus", 0);
if (ddc) {
panel->ddc = of_find_i2c_adapter_by_node(ddc);
of_node_put(ddc);
- if (!panel->ddc) {
- err = -EPROBE_DEFER;
- goto free_backlight;
- }
+ if (!panel->ddc)
+ return -EPROBE_DEFER;
}
- drm_panel_init(&panel->base);
- panel->base.dev = dev;
- panel->base.funcs = &panel_simple_funcs;
+ if (!of_get_display_timing(dev->of_node, "panel-timing", &dt))
+ panel_simple_parse_panel_timing_node(dev, panel, &dt);
+
+ drm_panel_init(&panel->base, dev, &panel_simple_funcs,
+ desc->connector_type);
+
+ err = drm_panel_of_backlight(&panel->base);
+ if (err)
+ goto free_ddc;
err = drm_panel_add(&panel->base);
if (err < 0)
@@ -364,9 +458,6 @@ static int panel_simple_probe(struct device *dev, const struct panel_desc *desc)
free_ddc:
if (panel->ddc)
put_device(&panel->ddc->dev);
-free_backlight:
- if (panel->backlight)
- put_device(&panel->backlight->dev);
return err;
}
@@ -376,16 +467,12 @@ static int panel_simple_remove(struct device *dev)
struct panel_simple *panel = dev_get_drvdata(dev);
drm_panel_remove(&panel->base);
-
- panel_simple_disable(&panel->base);
- panel_simple_unprepare(&panel->base);
+ drm_panel_disable(&panel->base);
+ drm_panel_unprepare(&panel->base);
if (panel->ddc)
put_device(&panel->ddc->dev);
- if (panel->backlight)
- put_device(&panel->backlight->dev);
-
return 0;
}
@@ -393,8 +480,8 @@ static void panel_simple_shutdown(struct device *dev)
{
struct panel_simple *panel = dev_get_drvdata(dev);
- panel_simple_disable(&panel->base);
- panel_simple_unprepare(&panel->base);
+ drm_panel_disable(&panel->base);
+ drm_panel_unprepare(&panel->base);
}
static const struct drm_display_mode ampire_am_480272h3tmqw_t01h_mode = {
@@ -496,22 +583,21 @@ static const struct panel_desc auo_b101aw03 = {
},
};
-static const struct drm_display_mode auo_b101ean01_mode = {
- .clock = 72500,
- .hdisplay = 1280,
- .hsync_start = 1280 + 119,
- .hsync_end = 1280 + 119 + 32,
- .htotal = 1280 + 119 + 32 + 21,
- .vdisplay = 800,
- .vsync_start = 800 + 4,
- .vsync_end = 800 + 4 + 20,
- .vtotal = 800 + 4 + 20 + 8,
- .vrefresh = 60,
+static const struct display_timing auo_b101ean01_timing = {
+ .pixelclock = { 65300000, 72500000, 75000000 },
+ .hactive = { 1280, 1280, 1280 },
+ .hfront_porch = { 18, 119, 119 },
+ .hback_porch = { 21, 21, 21 },
+ .hsync_len = { 32, 32, 32 },
+ .vactive = { 800, 800, 800 },
+ .vfront_porch = { 4, 4, 4 },
+ .vback_porch = { 8, 8, 8 },
+ .vsync_len = { 18, 20, 20 },
};
static const struct panel_desc auo_b101ean01 = {
- .modes = &auo_b101ean01_mode,
- .num_modes = 1,
+ .timings = &auo_b101ean01_timing,
+ .num_timings = 1,
.bpc = 6,
.size = {
.width = 217,
@@ -543,6 +629,35 @@ static const struct panel_desc auo_b101xtn01 = {
},
};
+static const struct drm_display_mode auo_b116xak01_mode = {
+ .clock = 69300,
+ .hdisplay = 1366,
+ .hsync_start = 1366 + 48,
+ .hsync_end = 1366 + 48 + 32,
+ .htotal = 1366 + 48 + 32 + 10,
+ .vdisplay = 768,
+ .vsync_start = 768 + 4,
+ .vsync_end = 768 + 4 + 6,
+ .vtotal = 768 + 4 + 6 + 15,
+ .vrefresh = 60,
+ .flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC,
+};
+
+static const struct panel_desc auo_b116xak01 = {
+ .modes = &auo_b116xak01_mode,
+ .num_modes = 1,
+ .bpc = 6,
+ .size = {
+ .width = 256,
+ .height = 144,
+ },
+ .delay = {
+ .hpd_absent_delay = 200,
+ },
+ .bus_format = MEDIA_BUS_FMT_RGB666_1X18,
+ .connector_type = DRM_MODE_CONNECTOR_eDP,
+};
+
static const struct drm_display_mode auo_b116xw03_mode = {
.clock = 70589,
.hdisplay = 1366,
@@ -719,14 +834,15 @@ static const struct panel_desc auo_g133han01 = {
.unprepare = 1000,
},
.bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA,
+ .connector_type = DRM_MODE_CONNECTOR_LVDS,
};
static const struct display_timing auo_g185han01_timings = {
.pixelclock = { 120000000, 144000000, 175000000 },
.hactive = { 1920, 1920, 1920 },
- .hfront_porch = { 18, 60, 74 },
- .hback_porch = { 12, 44, 54 },
- .hsync_len = { 10, 24, 32 },
+ .hfront_porch = { 36, 120, 148 },
+ .hback_porch = { 24, 88, 108 },
+ .hsync_len = { 20, 48, 64 },
.vactive = { 1080, 1080, 1080 },
.vfront_porch = { 6, 10, 40 },
.vback_porch = { 2, 5, 20 },
@@ -748,6 +864,7 @@ static const struct panel_desc auo_g185han01 = {
.unprepare = 1000,
},
.bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,
+ .connector_type = DRM_MODE_CONNECTOR_LVDS,
};
static const struct display_timing auo_p320hvn03_timings = {
@@ -776,6 +893,7 @@ static const struct panel_desc auo_p320hvn03 = {
.unprepare = 500,
},
.bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,
+ .connector_type = DRM_MODE_CONNECTOR_LVDS,
};
static const struct drm_display_mode auo_t215hvn01_mode = {
@@ -919,6 +1037,38 @@ static const struct panel_desc boe_nv101wxmn51 = {
},
};
+static const struct drm_display_mode boe_nv140fhmn49_modes[] = {
+ {
+ .clock = 148500,
+ .hdisplay = 1920,
+ .hsync_start = 1920 + 48,
+ .hsync_end = 1920 + 48 + 32,
+ .htotal = 2200,
+ .vdisplay = 1080,
+ .vsync_start = 1080 + 3,
+ .vsync_end = 1080 + 3 + 5,
+ .vtotal = 1125,
+ .vrefresh = 60,
+ },
+};
+
+static const struct panel_desc boe_nv140fhmn49 = {
+ .modes = boe_nv140fhmn49_modes,
+ .num_modes = ARRAY_SIZE(boe_nv140fhmn49_modes),
+ .bpc = 6,
+ .size = {
+ .width = 309,
+ .height = 174,
+ },
+ .delay = {
+ .prepare = 210,
+ .enable = 50,
+ .unprepare = 160,
+ },
+ .bus_format = MEDIA_BUS_FMT_RGB666_1X18,
+ .connector_type = DRM_MODE_CONNECTOR_eDP,
+};
+
static const struct drm_display_mode cdtech_s043wq26h_ct7_mode = {
.clock = 9000,
.hdisplay = 480,
@@ -1091,6 +1241,7 @@ static const struct panel_desc dlc_dlc0700yzg_1 = {
.disable = 200,
},
.bus_format = MEDIA_BUS_FMT_RGB666_1X7X3_SPWG,
+ .connector_type = DRM_MODE_CONNECTOR_LVDS,
};
static const struct display_timing dlc_dlc1010gig_timing = {
@@ -1121,6 +1272,7 @@ static const struct panel_desc dlc_dlc1010gig = {
.unprepare = 60,
},
.bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,
+ .connector_type = DRM_MODE_CONNECTOR_LVDS,
};
static const struct drm_display_mode edt_et035012dm6_mode = {
@@ -1335,6 +1487,31 @@ static const struct panel_desc giantplus_gpg482739qs5 = {
.bus_format = MEDIA_BUS_FMT_RGB888_1X24,
};
+static const struct display_timing giantplus_gpm940b0_timing = {
+ .pixelclock = { 13500000, 27000000, 27500000 },
+ .hactive = { 320, 320, 320 },
+ .hfront_porch = { 14, 686, 718 },
+ .hback_porch = { 50, 70, 255 },
+ .hsync_len = { 1, 1, 1 },
+ .vactive = { 240, 240, 240 },
+ .vfront_porch = { 1, 1, 179 },
+ .vback_porch = { 1, 21, 31 },
+ .vsync_len = { 1, 1, 6 },
+ .flags = DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW,
+};
+
+static const struct panel_desc giantplus_gpm940b0 = {
+ .timings = &giantplus_gpm940b0_timing,
+ .num_timings = 1,
+ .bpc = 8,
+ .size = {
+ .width = 60,
+ .height = 45,
+ },
+ .bus_format = MEDIA_BUS_FMT_RGB888_3X8,
+ .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_NEGEDGE,
+};
+
static const struct display_timing hannstar_hsd070pww1_timing = {
.pixelclock = { 64300000, 71100000, 82000000 },
.hactive = { 1280, 1280, 1280 },
@@ -1362,6 +1539,7 @@ static const struct panel_desc hannstar_hsd070pww1 = {
.height = 94,
},
.bus_format = MEDIA_BUS_FMT_RGB666_1X7X3_SPWG,
+ .connector_type = DRM_MODE_CONNECTOR_LVDS,
};
static const struct display_timing hannstar_hsd100pxn1_timing = {
@@ -1386,6 +1564,7 @@ static const struct panel_desc hannstar_hsd100pxn1 = {
.height = 152,
},
.bus_format = MEDIA_BUS_FMT_RGB666_1X7X3_SPWG,
+ .connector_type = DRM_MODE_CONNECTOR_LVDS,
};
static const struct drm_display_mode hitachi_tx23d38vm0caa_mode = {
@@ -1492,6 +1671,7 @@ static const struct panel_desc innolux_g070y2_l01 = {
.unprepare = 800,
},
.bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,
+ .connector_type = DRM_MODE_CONNECTOR_LVDS,
};
static const struct display_timing innolux_g101ice_l01_timing = {
@@ -1520,6 +1700,7 @@ static const struct panel_desc innolux_g101ice_l01 = {
.disable = 200,
},
.bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,
+ .connector_type = DRM_MODE_CONNECTOR_LVDS,
};
static const struct display_timing innolux_g121i1_l01_timing = {
@@ -1547,6 +1728,7 @@ static const struct panel_desc innolux_g121i1_l01 = {
.disable = 20,
},
.bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,
+ .connector_type = DRM_MODE_CONNECTOR_LVDS,
};
static const struct drm_display_mode innolux_g121x1_l03_mode = {
@@ -1578,23 +1760,32 @@ static const struct panel_desc innolux_g121x1_l03 = {
},
};
-static const struct drm_display_mode innolux_n116bge_mode = {
- .clock = 76420,
- .hdisplay = 1366,
- .hsync_start = 1366 + 136,
- .hsync_end = 1366 + 136 + 30,
- .htotal = 1366 + 136 + 30 + 60,
- .vdisplay = 768,
- .vsync_start = 768 + 8,
- .vsync_end = 768 + 8 + 12,
- .vtotal = 768 + 8 + 12 + 12,
- .vrefresh = 60,
- .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
+/*
+ * Datasheet specifies that at 60 Hz refresh rate:
+ * - total horizontal time: { 1506, 1592, 1716 }
+ * - total vertical time: { 788, 800, 868 }
+ *
+ * ...but doesn't go into exactly how that should be split into a front
+ * porch, back porch, or sync length. For now we'll leave a single setting
+ * here which allows a bit of tweaking of the pixel clock at the expense of
+ * refresh rate.
+ */
+static const struct display_timing innolux_n116bge_timing = {
+ .pixelclock = { 72600000, 76420000, 80240000 },
+ .hactive = { 1366, 1366, 1366 },
+ .hfront_porch = { 136, 136, 136 },
+ .hback_porch = { 60, 60, 60 },
+ .hsync_len = { 30, 30, 30 },
+ .vactive = { 768, 768, 768 },
+ .vfront_porch = { 8, 8, 8 },
+ .vback_porch = { 12, 12, 12 },
+ .vsync_len = { 12, 12, 12 },
+ .flags = DISPLAY_FLAGS_VSYNC_LOW | DISPLAY_FLAGS_HSYNC_LOW,
};
static const struct panel_desc innolux_n116bge = {
- .modes = &innolux_n116bge_mode,
- .num_modes = 1,
+ .timings = &innolux_n116bge_timing,
+ .num_timings = 1,
.bpc = 6,
.size = {
.width = 256,
@@ -1721,6 +1912,7 @@ static const struct panel_desc koe_tx31d200vm0baa = {
.height = 109,
},
.bus_format = MEDIA_BUS_FMT_RGB666_1X7X3_SPWG,
+ .connector_type = DRM_MODE_CONNECTOR_LVDS,
};
static const struct display_timing kyo_tcg121xglp_timing = {
@@ -1745,6 +1937,7 @@ static const struct panel_desc kyo_tcg121xglp = {
.height = 184,
},
.bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,
+ .connector_type = DRM_MODE_CONNECTOR_LVDS,
};
static const struct drm_display_mode lemaker_bl035_rgb_002_mode = {
@@ -1793,6 +1986,7 @@ static const struct panel_desc lg_lb070wv8 = {
.height = 91,
},
.bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,
+ .connector_type = DRM_MODE_CONNECTOR_LVDS,
};
static const struct drm_display_mode lg_lp079qx1_sp0v_mode = {
@@ -1900,6 +2094,40 @@ static const struct drm_display_mode mitsubishi_aa070mc01_mode = {
.flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
};
+static const struct drm_display_mode logicpd_type_28_mode = {
+ .clock = 9000,
+ .hdisplay = 480,
+ .hsync_start = 480 + 3,
+ .hsync_end = 480 + 3 + 42,
+ .htotal = 480 + 3 + 42 + 2,
+
+ .vdisplay = 272,
+ .vsync_start = 272 + 2,
+ .vsync_end = 272 + 2 + 11,
+ .vtotal = 272 + 2 + 11 + 3,
+ .vrefresh = 60,
+ .flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC,
+};
+
+static const struct panel_desc logicpd_type_28 = {
+ .modes = &logicpd_type_28_mode,
+ .num_modes = 1,
+ .bpc = 8,
+ .size = {
+ .width = 105,
+ .height = 67,
+ },
+ .delay = {
+ .prepare = 200,
+ .enable = 200,
+ .unprepare = 200,
+ .disable = 200,
+ },
+ .bus_format = MEDIA_BUS_FMT_RGB888_1X24,
+ .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE |
+ DRM_BUS_FLAG_SYNC_DRIVE_NEGEDGE,
+};
+
static const struct panel_desc mitsubishi_aa070mc01 = {
.modes = &mitsubishi_aa070mc01_mode,
.num_modes = 1,
@@ -1915,6 +2143,7 @@ static const struct panel_desc mitsubishi_aa070mc01 = {
.disable = 400,
},
.bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,
+ .connector_type = DRM_MODE_CONNECTOR_LVDS,
.bus_flags = DRM_BUS_FLAG_DE_HIGH,
};
@@ -1943,6 +2172,7 @@ static const struct panel_desc nec_nl12880bc20_05 = {
.disable = 50,
},
.bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,
+ .connector_type = DRM_MODE_CONNECTOR_LVDS,
};
static const struct drm_display_mode nec_nl4827hc19_05b_mode = {
@@ -2045,6 +2275,7 @@ static const struct panel_desc nlt_nl192108ac18_02d = {
.unprepare = 500,
},
.bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,
+ .connector_type = DRM_MODE_CONNECTOR_LVDS,
};
static const struct drm_display_mode nvd_9128_mode = {
@@ -2068,6 +2299,7 @@ static const struct panel_desc nvd_9128 = {
.height = 88,
},
.bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,
+ .connector_type = DRM_MODE_CONNECTOR_LVDS,
};
static const struct display_timing okaya_rs800480t_7x0gp_timing = {
@@ -2157,6 +2389,33 @@ static const struct panel_desc ontat_yx700wv03 = {
.bus_format = MEDIA_BUS_FMT_RGB666_1X18,
};
+static const struct drm_display_mode ortustech_com37h3m_mode = {
+ .clock = 22153,
+ .hdisplay = 480,
+ .hsync_start = 480 + 8,
+ .hsync_end = 480 + 8 + 10,
+ .htotal = 480 + 8 + 10 + 10,
+ .vdisplay = 640,
+ .vsync_start = 640 + 4,
+ .vsync_end = 640 + 4 + 3,
+ .vtotal = 640 + 4 + 3 + 4,
+ .vrefresh = 60,
+ .flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC,
+};
+
+static const struct panel_desc ortustech_com37h3m = {
+ .modes = &ortustech_com37h3m_mode,
+ .num_modes = 1,
+ .bpc = 8,
+ .size = {
+ .width = 56, /* 56.16mm */
+ .height = 75, /* 74.88mm */
+ },
+ .bus_format = MEDIA_BUS_FMT_RGB888_1X24,
+ .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_POSEDGE |
+ DRM_BUS_FLAG_SYNC_DRIVE_POSEDGE,
+};
+
static const struct drm_display_mode ortustech_com43h4m85ulc_mode = {
.clock = 25000,
.hdisplay = 480,
@@ -2206,6 +2465,7 @@ static const struct panel_desc osddisplays_osd070t1718_19ts = {
},
.bus_format = MEDIA_BUS_FMT_RGB888_1X24,
.bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE,
+ .connector_type = DRM_MODE_CONNECTOR_DPI,
};
static const struct drm_display_mode pda_91_00156_a0_mode = {
@@ -2354,6 +2614,83 @@ static const struct panel_desc samsung_ltn140at29_301 = {
},
};
+static const struct display_timing satoz_sat050at40h12r2_timing = {
+ .pixelclock = {33300000, 33300000, 50000000},
+ .hactive = {800, 800, 800},
+ .hfront_porch = {16, 210, 354},
+ .hback_porch = {46, 46, 46},
+ .hsync_len = {1, 1, 40},
+ .vactive = {480, 480, 480},
+ .vfront_porch = {7, 22, 147},
+ .vback_porch = {23, 23, 23},
+ .vsync_len = {1, 1, 20},
+};
+
+static const struct panel_desc satoz_sat050at40h12r2 = {
+ .timings = &satoz_sat050at40h12r2_timing,
+ .num_timings = 1,
+ .bpc = 8,
+ .size = {
+ .width = 108,
+ .height = 65,
+ },
+ .bus_format = MEDIA_BUS_FMT_RGB888_1X24,
+ .connector_type = DRM_MODE_CONNECTOR_LVDS,
+};
+
+static const struct drm_display_mode sharp_ld_d5116z01b_mode = {
+ .clock = 168480,
+ .hdisplay = 1920,
+ .hsync_start = 1920 + 48,
+ .hsync_end = 1920 + 48 + 32,
+ .htotal = 1920 + 48 + 32 + 80,
+ .vdisplay = 1280,
+ .vsync_start = 1280 + 3,
+ .vsync_end = 1280 + 3 + 10,
+ .vtotal = 1280 + 3 + 10 + 57,
+ .vrefresh = 60,
+ .flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC,
+};
+
+static const struct panel_desc sharp_ld_d5116z01b = {
+ .modes = &sharp_ld_d5116z01b_mode,
+ .num_modes = 1,
+ .bpc = 8,
+ .size = {
+ .width = 260,
+ .height = 120,
+ },
+ .bus_format = MEDIA_BUS_FMT_RGB888_1X24,
+ .bus_flags = DRM_BUS_FLAG_DATA_MSB_TO_LSB,
+};
+
+static const struct drm_display_mode sharp_lq070y3dg3b_mode = {
+ .clock = 33260,
+ .hdisplay = 800,
+ .hsync_start = 800 + 64,
+ .hsync_end = 800 + 64 + 128,
+ .htotal = 800 + 64 + 128 + 64,
+ .vdisplay = 480,
+ .vsync_start = 480 + 8,
+ .vsync_end = 480 + 8 + 2,
+ .vtotal = 480 + 8 + 2 + 35,
+ .vrefresh = 60,
+ .flags = DISPLAY_FLAGS_PIXDATA_POSEDGE,
+};
+
+static const struct panel_desc sharp_lq070y3dg3b = {
+ .modes = &sharp_lq070y3dg3b_mode,
+ .num_modes = 1,
+ .bpc = 8,
+ .size = {
+ .width = 152, /* 152.4mm */
+ .height = 91, /* 91.4mm */
+ },
+ .bus_format = MEDIA_BUS_FMT_RGB888_1X24,
+ .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_POSEDGE |
+ DRM_BUS_FLAG_SYNC_DRIVE_POSEDGE,
+};
+
static const struct drm_display_mode sharp_lq035q7db03_mode = {
.clock = 5500,
.hdisplay = 240,
@@ -2400,6 +2737,7 @@ static const struct panel_desc sharp_lq101k1ly04 = {
.height = 136,
},
.bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA,
+ .connector_type = DRM_MODE_CONNECTOR_LVDS,
};
static const struct display_timing sharp_lq123p1jx31_timing = {
@@ -2454,6 +2792,33 @@ static const struct panel_desc sharp_lq150x1lg11 = {
.bus_format = MEDIA_BUS_FMT_RGB565_1X16,
};
+static const struct display_timing sharp_ls020b1dd01d_timing = {
+ .pixelclock = { 2000000, 4200000, 5000000 },
+ .hactive = { 240, 240, 240 },
+ .hfront_porch = { 66, 66, 66 },
+ .hback_porch = { 1, 1, 1 },
+ .hsync_len = { 1, 1, 1 },
+ .vactive = { 160, 160, 160 },
+ .vfront_porch = { 52, 52, 52 },
+ .vback_porch = { 6, 6, 6 },
+ .vsync_len = { 10, 10, 10 },
+ .flags = DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_LOW,
+};
+
+static const struct panel_desc sharp_ls020b1dd01d = {
+ .timings = &sharp_ls020b1dd01d_timing,
+ .num_timings = 1,
+ .bpc = 6,
+ .size = {
+ .width = 42,
+ .height = 28,
+ },
+ .bus_format = MEDIA_BUS_FMT_RGB565_1X16,
+ .bus_flags = DRM_BUS_FLAG_DE_HIGH
+ | DRM_BUS_FLAG_PIXDATA_NEGEDGE
+ | DRM_BUS_FLAG_SHARP_SIGNALS,
+};
+
static const struct drm_display_mode shelly_sca07010_bfn_lnn_mode = {
.clock = 33300,
.hdisplay = 800,
@@ -2552,6 +2917,7 @@ static const struct panel_desc tianma_tm070jdhg30 = {
.height = 95,
},
.bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,
+ .connector_type = DRM_MODE_CONNECTOR_LVDS,
};
static const struct display_timing tianma_tm070rvhg71_timing = {
@@ -2576,6 +2942,65 @@ static const struct panel_desc tianma_tm070rvhg71 = {
.height = 86,
},
.bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,
+ .connector_type = DRM_MODE_CONNECTOR_LVDS,
+};
+
+static const struct drm_display_mode ti_nspire_cx_lcd_mode[] = {
+ {
+ .clock = 10000,
+ .hdisplay = 320,
+ .hsync_start = 320 + 50,
+ .hsync_end = 320 + 50 + 6,
+ .htotal = 320 + 50 + 6 + 38,
+ .vdisplay = 240,
+ .vsync_start = 240 + 3,
+ .vsync_end = 240 + 3 + 1,
+ .vtotal = 240 + 3 + 1 + 17,
+ .vrefresh = 60,
+ .flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC,
+ },
+};
+
+static const struct panel_desc ti_nspire_cx_lcd_panel = {
+ .modes = ti_nspire_cx_lcd_mode,
+ .num_modes = 1,
+ .bpc = 8,
+ .size = {
+ .width = 65,
+ .height = 49,
+ },
+ .bus_format = MEDIA_BUS_FMT_RGB888_1X24,
+ .bus_flags = DRM_BUS_FLAG_PIXDATA_NEGEDGE,
+};
+
+static const struct drm_display_mode ti_nspire_classic_lcd_mode[] = {
+ {
+ .clock = 10000,
+ .hdisplay = 320,
+ .hsync_start = 320 + 6,
+ .hsync_end = 320 + 6 + 6,
+ .htotal = 320 + 6 + 6 + 6,
+ .vdisplay = 240,
+ .vsync_start = 240 + 0,
+ .vsync_end = 240 + 0 + 1,
+ .vtotal = 240 + 0 + 1 + 0,
+ .vrefresh = 60,
+ .flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC,
+ },
+};
+
+static const struct panel_desc ti_nspire_classic_lcd_panel = {
+ .modes = ti_nspire_classic_lcd_mode,
+ .num_modes = 1,
+ /* The grayscale panel has 8 bit for the color .. Y (black) */
+ .bpc = 8,
+ .size = {
+ .width = 71,
+ .height = 53,
+ },
+ /* This is the grayscale bus format */
+ .bus_format = MEDIA_BUS_FMT_Y8_1X8,
+ .bus_flags = DRM_BUS_FLAG_PIXDATA_POSEDGE,
};
static const struct drm_display_mode toshiba_lt089ac29000_mode = {
@@ -2600,6 +3025,7 @@ static const struct panel_desc toshiba_lt089ac29000 = {
},
.bus_format = MEDIA_BUS_FMT_RGB888_1X24,
.bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE,
+ .connector_type = DRM_MODE_CONNECTOR_LVDS,
};
static const struct drm_display_mode tpk_f07a_0102_mode = {
@@ -2670,6 +3096,7 @@ static const struct panel_desc urt_umsh_8596md_lvds = {
.height = 91,
},
.bus_format = MEDIA_BUS_FMT_RGB666_1X7X3_SPWG,
+ .connector_type = DRM_MODE_CONNECTOR_LVDS,
};
static const struct panel_desc urt_umsh_8596md_parallel = {
@@ -2784,6 +3211,9 @@ static const struct of_device_id platform_of_match[] = {
.compatible = "auo,b101xtn01",
.data = &auo_b101xtn01,
}, {
+ .compatible = "auo,b116xa01",
+ .data = &auo_b116xak01,
+ }, {
.compatible = "auo,b116xw03",
.data = &auo_b116xw03,
}, {
@@ -2826,6 +3256,9 @@ static const struct of_device_id platform_of_match[] = {
.compatible = "boe,nv101wxmn51",
.data = &boe_nv101wxmn51,
}, {
+ .compatible = "boe,nv140fhmn49",
+ .data = &boe_nv140fhmn49,
+ }, {
.compatible = "cdtech,s043wq26h-ct7",
.data = &cdtech_s043wq26h_ct7,
}, {
@@ -2883,6 +3316,9 @@ static const struct of_device_id platform_of_match[] = {
.compatible = "giantplus,gpg482739qs5",
.data = &giantplus_gpg482739qs5
}, {
+ .compatible = "giantplus,gpm940b0",
+ .data = &giantplus_gpm940b0,
+ }, {
.compatible = "hannstar,hsd070pww1",
.data = &hannstar_hsd070pww1,
}, {
@@ -2949,6 +3385,9 @@ static const struct of_device_id platform_of_match[] = {
.compatible = "lg,lp129qe",
.data = &lg_lp129qe,
}, {
+ .compatible = "logicpd,type28",
+ .data = &logicpd_type_28,
+ }, {
.compatible = "mitsubishi,aa070mc01-ca1",
.data = &mitsubishi_aa070mc01,
}, {
@@ -2979,6 +3418,12 @@ static const struct of_device_id platform_of_match[] = {
.compatible = "ontat,yx700wv03",
.data = &ontat_yx700wv03,
}, {
+ .compatible = "ortustech,com37h3m05dtc",
+ .data = &ortustech_com37h3m,
+ }, {
+ .compatible = "ortustech,com37h3m99dtc",
+ .data = &ortustech_com37h3m,
+ }, {
.compatible = "ortustech,com43h4m85ulc",
.data = &ortustech_com43h4m85ulc,
}, {
@@ -3003,9 +3448,18 @@ static const struct of_device_id platform_of_match[] = {
.compatible = "samsung,ltn140at29-301",
.data = &samsung_ltn140at29_301,
}, {
+ .compatible = "satoz,sat050at40h12r2",
+ .data = &satoz_sat050at40h12r2,
+ }, {
+ .compatible = "sharp,ld-d5116z01b",
+ .data = &sharp_ld_d5116z01b,
+ }, {
.compatible = "sharp,lq035q7db03",
.data = &sharp_lq035q7db03,
}, {
+ .compatible = "sharp,lq070y3dg3b",
+ .data = &sharp_lq070y3dg3b,
+ }, {
.compatible = "sharp,lq101k1ly04",
.data = &sharp_lq101k1ly04,
}, {
@@ -3015,6 +3469,9 @@ static const struct of_device_id platform_of_match[] = {
.compatible = "sharp,lq150x1lg11",
.data = &sharp_lq150x1lg11,
}, {
+ .compatible = "sharp,ls020b1dd01d",
+ .data = &sharp_ls020b1dd01d,
+ }, {
.compatible = "shelly,sca07010-bfn-lnn",
.data = &shelly_sca07010_bfn_lnn,
}, {
@@ -3030,6 +3487,12 @@ static const struct of_device_id platform_of_match[] = {
.compatible = "tianma,tm070rvhg71",
.data = &tianma_tm070rvhg71,
}, {
+ .compatible = "ti,nspire-cx-lcd-panel",
+ .data = &ti_nspire_cx_lcd_panel,
+ }, {
+ .compatible = "ti,nspire-classic-lcd-panel",
+ .data = &ti_nspire_classic_lcd_panel,
+ }, {
.compatible = "toshiba,lt089ac29000",
.data = &toshiba_lt089ac29000,
}, {
diff --git a/drivers/gpu/drm/panel/panel-sitronix-st7701.c b/drivers/gpu/drm/panel/panel-sitronix-st7701.c
index 09c5d9a6f9fa..4b4f2558e3b4 100644
--- a/drivers/gpu/drm/panel/panel-sitronix-st7701.c
+++ b/drivers/gpu/drm/panel/panel-sitronix-st7701.c
@@ -9,7 +9,6 @@
#include <drm/drm_panel.h>
#include <drm/drm_print.h>
-#include <linux/backlight.h>
#include <linux/gpio/consumer.h>
#include <linux/delay.h>
#include <linux/module.h>
@@ -103,7 +102,6 @@ struct st7701 {
struct mipi_dsi_device *dsi;
const struct st7701_panel_desc *desc;
- struct backlight_device *backlight;
struct regulator_bulk_data *supplies;
struct gpio_desc *reset;
unsigned int sleep_delay;
@@ -223,7 +221,6 @@ static int st7701_enable(struct drm_panel *panel)
struct st7701 *st7701 = panel_to_st7701(panel);
ST7701_DSI(st7701, MIPI_DCS_SET_DISPLAY_ON, 0x00);
- backlight_enable(st7701->backlight);
return 0;
}
@@ -232,7 +229,6 @@ static int st7701_disable(struct drm_panel *panel)
{
struct st7701 *st7701 = panel_to_st7701(panel);
- backlight_disable(st7701->backlight);
ST7701_DSI(st7701, MIPI_DCS_SET_DISPLAY_OFF, 0x00);
return 0;
@@ -264,13 +260,14 @@ static int st7701_unprepare(struct drm_panel *panel)
return 0;
}
-static int st7701_get_modes(struct drm_panel *panel)
+static int st7701_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
{
struct st7701 *st7701 = panel_to_st7701(panel);
const struct drm_display_mode *desc_mode = st7701->desc->mode;
struct drm_display_mode *mode;
- mode = drm_mode_duplicate(panel->drm, desc_mode);
+ mode = drm_mode_duplicate(connector->dev, desc_mode);
if (!mode) {
DRM_DEV_ERROR(&st7701->dsi->dev,
"failed to add mode %ux%ux@%u\n",
@@ -280,10 +277,10 @@ static int st7701_get_modes(struct drm_panel *panel)
}
drm_mode_set_name(mode);
- drm_mode_probed_add(panel->connector, mode);
+ drm_mode_probed_add(connector, mode);
- panel->connector->display_info.width_mm = desc_mode->width_mm;
- panel->connector->display_info.height_mm = desc_mode->height_mm;
+ connector->display_info.width_mm = desc_mode->width_mm;
+ connector->display_info.height_mm = desc_mode->height_mm;
return 1;
}
@@ -365,11 +362,8 @@ static int st7701_dsi_probe(struct mipi_dsi_device *dsi)
return PTR_ERR(st7701->reset);
}
- st7701->backlight = devm_of_find_backlight(&dsi->dev);
- if (IS_ERR(st7701->backlight))
- return PTR_ERR(st7701->backlight);
-
- drm_panel_init(&st7701->panel);
+ drm_panel_init(&st7701->panel, &dsi->dev, &st7701_funcs,
+ DRM_MODE_CONNECTOR_DSI);
/**
* Once sleep out has been issued, ST7701 IC required to wait 120ms
@@ -381,8 +375,10 @@ static int st7701_dsi_probe(struct mipi_dsi_device *dsi)
* ts8550b and there is no valid documentation for that.
*/
st7701->sleep_delay = 120 + desc->panel_sleep_delay;
- st7701->panel.funcs = &st7701_funcs;
- st7701->panel.dev = &dsi->dev;
+
+ ret = drm_panel_of_backlight(&st7701->panel);
+ if (ret)
+ return ret;
ret = drm_panel_add(&st7701->panel);
if (ret < 0)
diff --git a/drivers/gpu/drm/panel/panel-sitronix-st7789v.c b/drivers/gpu/drm/panel/panel-sitronix-st7789v.c
index 5e3e92ea9ea6..cc02c54c1b2e 100644
--- a/drivers/gpu/drm/panel/panel-sitronix-st7789v.c
+++ b/drivers/gpu/drm/panel/panel-sitronix-st7789v.c
@@ -3,7 +3,6 @@
* Copyright (C) 2017 Free Electrons
*/
-#include <linux/backlight.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/module.h>
@@ -116,7 +115,6 @@ struct st7789v {
struct drm_panel panel;
struct spi_device *spi;
struct gpio_desc *reset;
- struct backlight_device *backlight;
struct regulator *power;
};
@@ -170,14 +168,14 @@ static const struct drm_display_mode default_mode = {
.vrefresh = 60,
};
-static int st7789v_get_modes(struct drm_panel *panel)
+static int st7789v_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
{
- struct drm_connector *connector = panel->connector;
struct drm_display_mode *mode;
- mode = drm_mode_duplicate(panel->drm, &default_mode);
+ mode = drm_mode_duplicate(connector->dev, &default_mode);
if (!mode) {
- dev_err(panel->drm->dev, "failed to add mode %ux%ux@%u\n",
+ dev_err(panel->dev, "failed to add mode %ux%ux@%u\n",
default_mode.hdisplay, default_mode.vdisplay,
default_mode.vrefresh);
return -ENOMEM;
@@ -188,8 +186,8 @@ static int st7789v_get_modes(struct drm_panel *panel)
mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
drm_mode_probed_add(connector, mode);
- panel->connector->display_info.width_mm = 61;
- panel->connector->display_info.height_mm = 103;
+ connector->display_info.width_mm = 61;
+ connector->display_info.height_mm = 103;
return 1;
}
@@ -323,12 +321,6 @@ static int st7789v_enable(struct drm_panel *panel)
{
struct st7789v *ctx = panel_to_st7789v(panel);
- if (ctx->backlight) {
- ctx->backlight->props.state &= ~BL_CORE_FBBLANK;
- ctx->backlight->props.power = FB_BLANK_UNBLANK;
- backlight_update_status(ctx->backlight);
- }
-
return st7789v_write_command(ctx, MIPI_DCS_SET_DISPLAY_ON);
}
@@ -339,12 +331,6 @@ static int st7789v_disable(struct drm_panel *panel)
ST7789V_TEST(ret, st7789v_write_command(ctx, MIPI_DCS_SET_DISPLAY_OFF));
- if (ctx->backlight) {
- ctx->backlight->props.power = FB_BLANK_POWERDOWN;
- ctx->backlight->props.state |= BL_CORE_FBBLANK;
- backlight_update_status(ctx->backlight);
- }
-
return 0;
}
@@ -370,7 +356,6 @@ static const struct drm_panel_funcs st7789v_drm_funcs = {
static int st7789v_probe(struct spi_device *spi)
{
- struct device_node *backlight;
struct st7789v *ctx;
int ret;
@@ -381,8 +366,8 @@ static int st7789v_probe(struct spi_device *spi)
spi_set_drvdata(spi, ctx);
ctx->spi = spi;
- ctx->panel.dev = &spi->dev;
- ctx->panel.funcs = &st7789v_drm_funcs;
+ drm_panel_init(&ctx->panel, &spi->dev, &st7789v_drm_funcs,
+ DRM_MODE_CONNECTOR_DPI);
ctx->power = devm_regulator_get(&spi->dev, "power");
if (IS_ERR(ctx->power))
@@ -394,26 +379,15 @@ static int st7789v_probe(struct spi_device *spi)
return PTR_ERR(ctx->reset);
}
- backlight = of_parse_phandle(spi->dev.of_node, "backlight", 0);
- if (backlight) {
- ctx->backlight = of_find_backlight_by_node(backlight);
- of_node_put(backlight);
-
- if (!ctx->backlight)
- return -EPROBE_DEFER;
- }
+ ret = drm_panel_of_backlight(&ctx->panel);
+ if (ret)
+ return ret;
ret = drm_panel_add(&ctx->panel);
if (ret < 0)
- goto err_free_backlight;
+ return ret;
return 0;
-
-err_free_backlight:
- if (ctx->backlight)
- put_device(&ctx->backlight->dev);
-
- return ret;
}
static int st7789v_remove(struct spi_device *spi)
@@ -422,9 +396,6 @@ static int st7789v_remove(struct spi_device *spi)
drm_panel_remove(&ctx->panel);
- if (ctx->backlight)
- put_device(&ctx->backlight->dev);
-
return 0;
}
diff --git a/drivers/gpu/drm/panel/panel-sony-acx424akp.c b/drivers/gpu/drm/panel/panel-sony-acx424akp.c
new file mode 100644
index 000000000000..de0abf76ae6f
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-sony-acx424akp.c
@@ -0,0 +1,550 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * MIPI-DSI Sony ACX424AKP panel driver. This is a 480x864
+ * AMOLED panel with a command-only DSI interface.
+ *
+ * Copyright (C) Linaro Ltd. 2019
+ * Author: Linus Walleij
+ * Based on code and know-how from Marcus Lorentzon
+ * Copyright (C) ST-Ericsson SA 2010
+ */
+#include <linux/backlight.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.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 ACX424_DCS_READ_ID1 0xDA
+#define ACX424_DCS_READ_ID2 0xDB
+#define ACX424_DCS_READ_ID3 0xDC
+#define ACX424_DCS_SET_MDDI 0xAE
+
+/*
+ * Sony seems to use vendor ID 0x81
+ */
+#define DISPLAY_SONY_ACX424AKP_ID1 0x811b
+#define DISPLAY_SONY_ACX424AKP_ID2 0x811a
+/*
+ * The third ID looks like a bug, vendor IDs begin at 0x80
+ * and panel 00 ... seems like default values.
+ */
+#define DISPLAY_SONY_ACX424AKP_ID3 0x8000
+
+struct acx424akp {
+ struct drm_panel panel;
+ struct device *dev;
+ struct backlight_device *bl;
+ struct regulator *supply;
+ struct gpio_desc *reset_gpio;
+ bool video_mode;
+};
+
+static const struct drm_display_mode sony_acx424akp_vid_mode = {
+ .clock = 330000,
+ .hdisplay = 480,
+ .hsync_start = 480 + 15,
+ .hsync_end = 480 + 15 + 0,
+ .htotal = 480 + 15 + 0 + 15,
+ .vdisplay = 864,
+ .vsync_start = 864 + 14,
+ .vsync_end = 864 + 14 + 1,
+ .vtotal = 864 + 14 + 1 + 11,
+ .vrefresh = 60,
+ .width_mm = 48,
+ .height_mm = 84,
+ .flags = DRM_MODE_FLAG_PVSYNC,
+};
+
+/*
+ * The timings are not very helpful as the display is used in
+ * command mode using the maximum HS frequency.
+ */
+static const struct drm_display_mode sony_acx424akp_cmd_mode = {
+ .clock = 420160,
+ .hdisplay = 480,
+ .hsync_start = 480 + 154,
+ .hsync_end = 480 + 154 + 16,
+ .htotal = 480 + 154 + 16 + 32,
+ .vdisplay = 864,
+ .vsync_start = 864 + 1,
+ .vsync_end = 864 + 1 + 1,
+ .vtotal = 864 + 1 + 1 + 1,
+ /*
+ * Some desired refresh rate, experiments at the maximum "pixel"
+ * clock speed (HS clock 420 MHz) yields around 117Hz.
+ */
+ .vrefresh = 60,
+ .width_mm = 48,
+ .height_mm = 84,
+};
+
+static inline struct acx424akp *panel_to_acx424akp(struct drm_panel *panel)
+{
+ return container_of(panel, struct acx424akp, panel);
+}
+
+#define FOSC 20 /* 20Mhz */
+#define SCALE_FACTOR_NS_DIV_MHZ 1000
+
+static int acx424akp_set_brightness(struct backlight_device *bl)
+{
+ struct acx424akp *acx = bl_get_data(bl);
+ struct mipi_dsi_device *dsi = to_mipi_dsi_device(acx->dev);
+ int period_ns = 1023;
+ int duty_ns = bl->props.brightness;
+ u8 pwm_ratio;
+ u8 pwm_div;
+ u8 par;
+ int ret;
+
+ /* Calculate the PWM duty cycle in n/256's */
+ pwm_ratio = max(((duty_ns * 256) / period_ns) - 1, 1);
+ pwm_div = max(1,
+ ((FOSC * period_ns) / 256) /
+ SCALE_FACTOR_NS_DIV_MHZ);
+
+ /* Set up PWM dutycycle ONE byte (differs from the standard) */
+ DRM_DEV_DEBUG(acx->dev, "calculated duty cycle %02x\n", pwm_ratio);
+ ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_DISPLAY_BRIGHTNESS,
+ &pwm_ratio, 1);
+ if (ret < 0) {
+ DRM_DEV_ERROR(acx->dev,
+ "failed to set display PWM ratio (%d)\n",
+ ret);
+ return ret;
+ }
+
+ /*
+ * Sequence to write PWMDIV:
+ * address data
+ * 0xF3 0xAA CMD2 Unlock
+ * 0x00 0x01 Enter CMD2 page 0
+ * 0X7D 0x01 No reload MTP of CMD2 P1
+ * 0x22 PWMDIV
+ * 0x7F 0xAA CMD2 page 1 lock
+ */
+ par = 0xaa;
+ ret = mipi_dsi_dcs_write(dsi, 0xf3, &par, 1);
+ if (ret < 0) {
+ DRM_DEV_ERROR(acx->dev,
+ "failed to unlock CMD 2 (%d)\n",
+ ret);
+ return ret;
+ }
+ par = 0x01;
+ ret = mipi_dsi_dcs_write(dsi, 0x00, &par, 1);
+ if (ret < 0) {
+ DRM_DEV_ERROR(acx->dev,
+ "failed to enter page 1 (%d)\n",
+ ret);
+ return ret;
+ }
+ par = 0x01;
+ ret = mipi_dsi_dcs_write(dsi, 0x7d, &par, 1);
+ if (ret < 0) {
+ DRM_DEV_ERROR(acx->dev,
+ "failed to disable MTP reload (%d)\n",
+ ret);
+ return ret;
+ }
+ ret = mipi_dsi_dcs_write(dsi, 0x22, &pwm_div, 1);
+ if (ret < 0) {
+ DRM_DEV_ERROR(acx->dev,
+ "failed to set PWM divisor (%d)\n",
+ ret);
+ return ret;
+ }
+ par = 0xaa;
+ ret = mipi_dsi_dcs_write(dsi, 0x7f, &par, 1);
+ if (ret < 0) {
+ DRM_DEV_ERROR(acx->dev,
+ "failed to lock CMD 2 (%d)\n",
+ ret);
+ return ret;
+ }
+
+ /* Enable backlight */
+ par = 0x24;
+ ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY,
+ &par, 1);
+ if (ret < 0) {
+ DRM_DEV_ERROR(acx->dev,
+ "failed to enable display backlight (%d)\n",
+ ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct backlight_ops acx424akp_bl_ops = {
+ .update_status = acx424akp_set_brightness,
+};
+
+static int acx424akp_read_id(struct acx424akp *acx)
+{
+ struct mipi_dsi_device *dsi = to_mipi_dsi_device(acx->dev);
+ u8 vendor, version, panel;
+ u16 val;
+ int ret;
+
+ ret = mipi_dsi_dcs_read(dsi, ACX424_DCS_READ_ID1, &vendor, 1);
+ if (ret < 0) {
+ DRM_DEV_ERROR(acx->dev, "could not vendor ID byte\n");
+ return ret;
+ }
+ ret = mipi_dsi_dcs_read(dsi, ACX424_DCS_READ_ID2, &version, 1);
+ if (ret < 0) {
+ DRM_DEV_ERROR(acx->dev, "could not read device version byte\n");
+ return ret;
+ }
+ ret = mipi_dsi_dcs_read(dsi, ACX424_DCS_READ_ID3, &panel, 1);
+ if (ret < 0) {
+ DRM_DEV_ERROR(acx->dev, "could not read panel ID byte\n");
+ return ret;
+ }
+
+ if (vendor == 0x00) {
+ DRM_DEV_ERROR(acx->dev, "device vendor ID is zero\n");
+ return -ENODEV;
+ }
+
+ val = (vendor << 8) | panel;
+ switch (val) {
+ case DISPLAY_SONY_ACX424AKP_ID1:
+ case DISPLAY_SONY_ACX424AKP_ID2:
+ case DISPLAY_SONY_ACX424AKP_ID3:
+ DRM_DEV_INFO(acx->dev,
+ "MTP vendor: %02x, version: %02x, panel: %02x\n",
+ vendor, version, panel);
+ break;
+ default:
+ DRM_DEV_INFO(acx->dev,
+ "unknown vendor: %02x, version: %02x, panel: %02x\n",
+ vendor, version, panel);
+ break;
+ }
+
+ return 0;
+}
+
+static int acx424akp_power_on(struct acx424akp *acx)
+{
+ int ret;
+
+ ret = regulator_enable(acx->supply);
+ if (ret) {
+ DRM_DEV_ERROR(acx->dev, "failed to enable supply (%d)\n", ret);
+ return ret;
+ }
+
+ /* Assert RESET */
+ gpiod_set_value_cansleep(acx->reset_gpio, 1);
+ udelay(20);
+ /* De-assert RESET */
+ gpiod_set_value_cansleep(acx->reset_gpio, 0);
+ usleep_range(11000, 20000);
+
+ return 0;
+}
+
+static void acx424akp_power_off(struct acx424akp *acx)
+{
+ /* Assert RESET */
+ gpiod_set_value_cansleep(acx->reset_gpio, 1);
+ usleep_range(11000, 20000);
+
+ regulator_disable(acx->supply);
+}
+
+static int acx424akp_prepare(struct drm_panel *panel)
+{
+ struct acx424akp *acx = panel_to_acx424akp(panel);
+ struct mipi_dsi_device *dsi = to_mipi_dsi_device(acx->dev);
+ const u8 mddi = 3;
+ int ret;
+
+ ret = acx424akp_power_on(acx);
+ if (ret)
+ return ret;
+
+ ret = acx424akp_read_id(acx);
+ if (ret) {
+ DRM_DEV_ERROR(acx->dev, "failed to read panel ID (%d)\n", ret);
+ goto err_power_off;
+ }
+
+ /* Enabe tearing mode: send TE (tearing effect) at VBLANK */
+ ret = mipi_dsi_dcs_set_tear_on(dsi,
+ MIPI_DSI_DCS_TEAR_MODE_VBLANK);
+ if (ret) {
+ DRM_DEV_ERROR(acx->dev, "failed to enable vblank TE (%d)\n",
+ ret);
+ goto err_power_off;
+ }
+
+ /*
+ * Set MDDI
+ *
+ * This presumably deactivates the Qualcomm MDDI interface and
+ * selects DSI, similar code is found in other drivers such as the
+ * Sharp LS043T1LE01 which makes us suspect that this panel may be
+ * using a Novatek NT35565 or similar display driver chip that shares
+ * this command. Due to the lack of documentation we cannot know for
+ * sure.
+ */
+ ret = mipi_dsi_dcs_write(dsi, ACX424_DCS_SET_MDDI,
+ &mddi, sizeof(mddi));
+ if (ret < 0) {
+ DRM_DEV_ERROR(acx->dev, "failed to set MDDI (%d)\n", ret);
+ goto err_power_off;
+ }
+
+ /* Exit sleep mode */
+ ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
+ if (ret) {
+ DRM_DEV_ERROR(acx->dev, "failed to exit sleep mode (%d)\n",
+ ret);
+ goto err_power_off;
+ }
+ msleep(140);
+
+ ret = mipi_dsi_dcs_set_display_on(dsi);
+ if (ret) {
+ DRM_DEV_ERROR(acx->dev, "failed to turn display on (%d)\n",
+ ret);
+ goto err_power_off;
+ }
+ if (acx->video_mode) {
+ /* In video mode turn peripheral on */
+ ret = mipi_dsi_turn_on_peripheral(dsi);
+ if (ret) {
+ dev_err(acx->dev, "failed to turn on peripheral\n");
+ goto err_power_off;
+ }
+ }
+
+ acx->bl->props.power = FB_BLANK_NORMAL;
+
+ return 0;
+
+err_power_off:
+ acx424akp_power_off(acx);
+ return ret;
+}
+
+static int acx424akp_unprepare(struct drm_panel *panel)
+{
+ struct acx424akp *acx = panel_to_acx424akp(panel);
+ struct mipi_dsi_device *dsi = to_mipi_dsi_device(acx->dev);
+ u8 par;
+ int ret;
+
+ /* Disable backlight */
+ par = 0x00;
+ ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY,
+ &par, 1);
+ if (ret) {
+ DRM_DEV_ERROR(acx->dev,
+ "failed to disable display backlight (%d)\n",
+ ret);
+ return ret;
+ }
+
+ ret = mipi_dsi_dcs_set_display_off(dsi);
+ if (ret) {
+ DRM_DEV_ERROR(acx->dev, "failed to turn display off (%d)\n",
+ ret);
+ return ret;
+ }
+
+ /* Enter sleep mode */
+ ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
+ if (ret) {
+ DRM_DEV_ERROR(acx->dev, "failed to enter sleep mode (%d)\n",
+ ret);
+ return ret;
+ }
+ msleep(85);
+
+ acx424akp_power_off(acx);
+ acx->bl->props.power = FB_BLANK_POWERDOWN;
+
+ return 0;
+}
+
+static int acx424akp_enable(struct drm_panel *panel)
+{
+ struct acx424akp *acx = panel_to_acx424akp(panel);
+
+ /*
+ * The backlight is on as long as the display is on
+ * so no use to call backlight_enable() here.
+ */
+ acx->bl->props.power = FB_BLANK_UNBLANK;
+
+ return 0;
+}
+
+static int acx424akp_disable(struct drm_panel *panel)
+{
+ struct acx424akp *acx = panel_to_acx424akp(panel);
+
+ /*
+ * The backlight is on as long as the display is on
+ * so no use to call backlight_disable() here.
+ */
+ acx->bl->props.power = FB_BLANK_NORMAL;
+
+ return 0;
+}
+
+static int acx424akp_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
+{
+ struct acx424akp *acx = panel_to_acx424akp(panel);
+ struct drm_display_mode *mode;
+
+ if (acx->video_mode)
+ mode = drm_mode_duplicate(connector->dev,
+ &sony_acx424akp_vid_mode);
+ else
+ mode = drm_mode_duplicate(connector->dev,
+ &sony_acx424akp_cmd_mode);
+ if (!mode) {
+ DRM_ERROR("bad mode or failed to add mode\n");
+ return -EINVAL;
+ }
+ 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; /* Number of modes */
+}
+
+static const struct drm_panel_funcs acx424akp_drm_funcs = {
+ .disable = acx424akp_disable,
+ .unprepare = acx424akp_unprepare,
+ .prepare = acx424akp_prepare,
+ .enable = acx424akp_enable,
+ .get_modes = acx424akp_get_modes,
+};
+
+static int acx424akp_probe(struct mipi_dsi_device *dsi)
+{
+ struct device *dev = &dsi->dev;
+ struct acx424akp *acx;
+ int ret;
+
+ acx = devm_kzalloc(dev, sizeof(struct acx424akp), GFP_KERNEL);
+ if (!acx)
+ return -ENOMEM;
+ acx->video_mode = of_property_read_bool(dev->of_node,
+ "enforce-video-mode");
+
+ mipi_dsi_set_drvdata(dsi, acx);
+ acx->dev = dev;
+
+ dsi->lanes = 2;
+ dsi->format = MIPI_DSI_FMT_RGB888;
+ /*
+ * FIXME: these come from the ST-Ericsson vendor driver for the
+ * HREF520 and seems to reflect limitations in the PLLs on that
+ * platform, if you have the datasheet, please cross-check the
+ * actual max rates.
+ */
+ dsi->lp_rate = 19200000;
+ dsi->hs_rate = 420160000;
+
+ if (acx->video_mode)
+ /* Burst mode using event for sync */
+ dsi->mode_flags =
+ MIPI_DSI_MODE_VIDEO |
+ MIPI_DSI_MODE_VIDEO_BURST;
+ else
+ dsi->mode_flags =
+ MIPI_DSI_CLOCK_NON_CONTINUOUS |
+ MIPI_DSI_MODE_EOT_PACKET;
+
+ acx->supply = devm_regulator_get(dev, "vddi");
+ if (IS_ERR(acx->supply))
+ return PTR_ERR(acx->supply);
+
+ /* This asserts RESET by default */
+ acx->reset_gpio = devm_gpiod_get_optional(dev, "reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(acx->reset_gpio)) {
+ ret = PTR_ERR(acx->reset_gpio);
+ if (ret != -EPROBE_DEFER)
+ DRM_DEV_ERROR(dev, "failed to request GPIO (%d)\n",
+ ret);
+ return ret;
+ }
+
+ drm_panel_init(&acx->panel, dev, &acx424akp_drm_funcs,
+ DRM_MODE_CONNECTOR_DSI);
+
+ acx->bl = devm_backlight_device_register(dev, "acx424akp", dev, acx,
+ &acx424akp_bl_ops, NULL);
+ if (IS_ERR(acx->bl)) {
+ DRM_DEV_ERROR(dev, "failed to register backlight device\n");
+ return PTR_ERR(acx->bl);
+ }
+ acx->bl->props.max_brightness = 1023;
+ acx->bl->props.brightness = 512;
+ acx->bl->props.power = FB_BLANK_POWERDOWN;
+
+ ret = drm_panel_add(&acx->panel);
+ if (ret < 0)
+ return ret;
+
+ ret = mipi_dsi_attach(dsi);
+ if (ret < 0) {
+ drm_panel_remove(&acx->panel);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int acx424akp_remove(struct mipi_dsi_device *dsi)
+{
+ struct acx424akp *acx = mipi_dsi_get_drvdata(dsi);
+
+ mipi_dsi_detach(dsi);
+ drm_panel_remove(&acx->panel);
+
+ return 0;
+}
+
+static const struct of_device_id acx424akp_of_match[] = {
+ { .compatible = "sony,acx424akp" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, acx424akp_of_match);
+
+static struct mipi_dsi_driver acx424akp_driver = {
+ .probe = acx424akp_probe,
+ .remove = acx424akp_remove,
+ .driver = {
+ .name = "panel-sony-acx424akp",
+ .of_match_table = acx424akp_of_match,
+ },
+};
+module_mipi_dsi_driver(acx424akp_driver);
+
+MODULE_AUTHOR("Linus Wallei <linus.walleij@linaro.org>");
+MODULE_DESCRIPTION("MIPI-DSI Sony acx424akp Panel Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/panel/panel-sony-acx565akm.c b/drivers/gpu/drm/panel/panel-sony-acx565akm.c
new file mode 100644
index 000000000000..5c4b6f6e5c2d
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-sony-acx565akm.c
@@ -0,0 +1,707 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Sony ACX565AKM LCD Panel driver
+ *
+ * Copyright (C) 2019 Texas Instruments Incorporated
+ *
+ * Based on the omapdrm-specific panel-sony-acx565akm driver
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Author: Imre Deak <imre.deak@nokia.com>
+ */
+
+/*
+ * TODO (to be addressed with hardware access to test the changes):
+ *
+ * - Update backlight support to use backlight_update_status() etc.
+ * - Use prepare/unprepare for the basic power on/off of the backligt
+ */
+
+#include <linux/backlight.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/spi/spi.h>
+#include <video/mipi_display.h>
+
+#include <drm/drm_connector.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_panel.h>
+
+#define CTRL_DISP_BRIGHTNESS_CTRL_ON BIT(5)
+#define CTRL_DISP_AMBIENT_LIGHT_CTRL_ON BIT(4)
+#define CTRL_DISP_BACKLIGHT_ON BIT(2)
+#define CTRL_DISP_AUTO_BRIGHTNESS_ON BIT(1)
+
+#define MIPID_CMD_WRITE_CABC 0x55
+#define MIPID_CMD_READ_CABC 0x56
+
+#define MIPID_VER_LPH8923 3
+#define MIPID_VER_LS041Y3 4
+#define MIPID_VER_L4F00311 8
+#define MIPID_VER_ACX565AKM 9
+
+struct acx565akm_panel {
+ struct drm_panel panel;
+
+ struct spi_device *spi;
+ struct gpio_desc *reset_gpio;
+ struct backlight_device *backlight;
+
+ struct mutex mutex;
+
+ const char *name;
+ u8 display_id[3];
+ int model;
+ int revision;
+ bool has_bc;
+ bool has_cabc;
+
+ bool enabled;
+ unsigned int cabc_mode;
+ /*
+ * Next value of jiffies when we can issue the next sleep in/out
+ * command.
+ */
+ unsigned long hw_guard_end;
+ unsigned long hw_guard_wait; /* max guard time in jiffies */
+};
+
+#define to_acx565akm_device(p) container_of(p, struct acx565akm_panel, panel)
+
+static void acx565akm_transfer(struct acx565akm_panel *lcd, int cmd,
+ const u8 *wbuf, int wlen, u8 *rbuf, int rlen)
+{
+ struct spi_message m;
+ struct spi_transfer *x, xfer[5];
+ int ret;
+
+ spi_message_init(&m);
+
+ memset(xfer, 0, sizeof(xfer));
+ x = &xfer[0];
+
+ cmd &= 0xff;
+ x->tx_buf = &cmd;
+ x->bits_per_word = 9;
+ x->len = 2;
+
+ if (rlen > 1 && wlen == 0) {
+ /*
+ * Between the command and the response data there is a
+ * dummy clock cycle. Add an extra bit after the command
+ * word to account for this.
+ */
+ x->bits_per_word = 10;
+ cmd <<= 1;
+ }
+ spi_message_add_tail(x, &m);
+
+ if (wlen) {
+ x++;
+ x->tx_buf = wbuf;
+ x->len = wlen;
+ x->bits_per_word = 9;
+ spi_message_add_tail(x, &m);
+ }
+
+ if (rlen) {
+ x++;
+ x->rx_buf = rbuf;
+ x->len = rlen;
+ spi_message_add_tail(x, &m);
+ }
+
+ ret = spi_sync(lcd->spi, &m);
+ if (ret < 0)
+ dev_dbg(&lcd->spi->dev, "spi_sync %d\n", ret);
+}
+
+static inline void acx565akm_cmd(struct acx565akm_panel *lcd, int cmd)
+{
+ acx565akm_transfer(lcd, cmd, NULL, 0, NULL, 0);
+}
+
+static inline void acx565akm_write(struct acx565akm_panel *lcd,
+ int reg, const u8 *buf, int len)
+{
+ acx565akm_transfer(lcd, reg, buf, len, NULL, 0);
+}
+
+static inline void acx565akm_read(struct acx565akm_panel *lcd,
+ int reg, u8 *buf, int len)
+{
+ acx565akm_transfer(lcd, reg, NULL, 0, buf, len);
+}
+
+/* -----------------------------------------------------------------------------
+ * Auto Brightness Control Via sysfs
+ */
+
+static unsigned int acx565akm_get_cabc_mode(struct acx565akm_panel *lcd)
+{
+ return lcd->cabc_mode;
+}
+
+static void acx565akm_set_cabc_mode(struct acx565akm_panel *lcd,
+ unsigned int mode)
+{
+ u16 cabc_ctrl;
+
+ lcd->cabc_mode = mode;
+ if (!lcd->enabled)
+ return;
+ cabc_ctrl = 0;
+ acx565akm_read(lcd, MIPID_CMD_READ_CABC, (u8 *)&cabc_ctrl, 1);
+ cabc_ctrl &= ~3;
+ cabc_ctrl |= (1 << 8) | (mode & 3);
+ acx565akm_write(lcd, MIPID_CMD_WRITE_CABC, (u8 *)&cabc_ctrl, 2);
+}
+
+static unsigned int acx565akm_get_hw_cabc_mode(struct acx565akm_panel *lcd)
+{
+ u8 cabc_ctrl;
+
+ acx565akm_read(lcd, MIPID_CMD_READ_CABC, &cabc_ctrl, 1);
+ return cabc_ctrl & 3;
+}
+
+static const char * const acx565akm_cabc_modes[] = {
+ "off", /* always used when CABC is not supported */
+ "ui",
+ "still-image",
+ "moving-image",
+};
+
+static ssize_t cabc_mode_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct acx565akm_panel *lcd = dev_get_drvdata(dev);
+ const char *mode_str;
+ int mode;
+
+ if (!lcd->has_cabc)
+ mode = 0;
+ else
+ mode = acx565akm_get_cabc_mode(lcd);
+
+ mode_str = "unknown";
+ if (mode >= 0 && mode < ARRAY_SIZE(acx565akm_cabc_modes))
+ mode_str = acx565akm_cabc_modes[mode];
+
+ return sprintf(buf, "%s\n", mode_str);
+}
+
+static ssize_t cabc_mode_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct acx565akm_panel *lcd = dev_get_drvdata(dev);
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(acx565akm_cabc_modes); i++) {
+ const char *mode_str = acx565akm_cabc_modes[i];
+ int cmp_len = strlen(mode_str);
+
+ if (count > 0 && buf[count - 1] == '\n')
+ count--;
+ if (count != cmp_len)
+ continue;
+
+ if (strncmp(buf, mode_str, cmp_len) == 0)
+ break;
+ }
+
+ if (i == ARRAY_SIZE(acx565akm_cabc_modes))
+ return -EINVAL;
+
+ if (!lcd->has_cabc && i != 0)
+ return -EINVAL;
+
+ mutex_lock(&lcd->mutex);
+ acx565akm_set_cabc_mode(lcd, i);
+ mutex_unlock(&lcd->mutex);
+
+ return count;
+}
+
+static ssize_t cabc_available_modes_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct acx565akm_panel *lcd = dev_get_drvdata(dev);
+ unsigned int i;
+ size_t len = 0;
+
+ if (!lcd->has_cabc)
+ return sprintf(buf, "%s\n", acx565akm_cabc_modes[0]);
+
+ for (i = 0; i < ARRAY_SIZE(acx565akm_cabc_modes); i++)
+ len += sprintf(&buf[len], "%s%s", i ? " " : "",
+ acx565akm_cabc_modes[i]);
+
+ buf[len++] = '\n';
+
+ return len;
+}
+
+static DEVICE_ATTR_RW(cabc_mode);
+static DEVICE_ATTR_RO(cabc_available_modes);
+
+static struct attribute *acx565akm_cabc_attrs[] = {
+ &dev_attr_cabc_mode.attr,
+ &dev_attr_cabc_available_modes.attr,
+ NULL,
+};
+
+static const struct attribute_group acx565akm_cabc_attr_group = {
+ .attrs = acx565akm_cabc_attrs,
+};
+
+/* -----------------------------------------------------------------------------
+ * Backlight Device
+ */
+
+static int acx565akm_get_actual_brightness(struct acx565akm_panel *lcd)
+{
+ u8 bv;
+
+ acx565akm_read(lcd, MIPI_DCS_GET_DISPLAY_BRIGHTNESS, &bv, 1);
+
+ return bv;
+}
+
+static void acx565akm_set_brightness(struct acx565akm_panel *lcd, int level)
+{
+ u16 ctrl;
+ int bv;
+
+ bv = level | (1 << 8);
+ acx565akm_write(lcd, MIPI_DCS_SET_DISPLAY_BRIGHTNESS, (u8 *)&bv, 2);
+
+ acx565akm_read(lcd, MIPI_DCS_GET_CONTROL_DISPLAY, (u8 *)&ctrl, 1);
+ if (level)
+ ctrl |= CTRL_DISP_BRIGHTNESS_CTRL_ON |
+ CTRL_DISP_BACKLIGHT_ON;
+ else
+ ctrl &= ~(CTRL_DISP_BRIGHTNESS_CTRL_ON |
+ CTRL_DISP_BACKLIGHT_ON);
+
+ ctrl |= 1 << 8;
+ acx565akm_write(lcd, MIPI_DCS_WRITE_CONTROL_DISPLAY, (u8 *)&ctrl, 2);
+}
+
+static int acx565akm_bl_update_status_locked(struct backlight_device *dev)
+{
+ struct acx565akm_panel *lcd = dev_get_drvdata(&dev->dev);
+ int level;
+
+ if (dev->props.fb_blank == FB_BLANK_UNBLANK &&
+ dev->props.power == FB_BLANK_UNBLANK)
+ level = dev->props.brightness;
+ else
+ level = 0;
+
+ acx565akm_set_brightness(lcd, level);
+
+ return 0;
+}
+
+static int acx565akm_bl_update_status(struct backlight_device *dev)
+{
+ struct acx565akm_panel *lcd = dev_get_drvdata(&dev->dev);
+ int ret;
+
+ mutex_lock(&lcd->mutex);
+ ret = acx565akm_bl_update_status_locked(dev);
+ mutex_unlock(&lcd->mutex);
+
+ return ret;
+}
+
+static int acx565akm_bl_get_intensity(struct backlight_device *dev)
+{
+ struct acx565akm_panel *lcd = dev_get_drvdata(&dev->dev);
+ unsigned int intensity;
+
+ mutex_lock(&lcd->mutex);
+
+ if (dev->props.fb_blank == FB_BLANK_UNBLANK &&
+ dev->props.power == FB_BLANK_UNBLANK)
+ intensity = acx565akm_get_actual_brightness(lcd);
+ else
+ intensity = 0;
+
+ mutex_unlock(&lcd->mutex);
+
+ return intensity;
+}
+
+static const struct backlight_ops acx565akm_bl_ops = {
+ .get_brightness = acx565akm_bl_get_intensity,
+ .update_status = acx565akm_bl_update_status,
+};
+
+static int acx565akm_backlight_init(struct acx565akm_panel *lcd)
+{
+ struct backlight_properties props = {
+ .fb_blank = FB_BLANK_UNBLANK,
+ .power = FB_BLANK_UNBLANK,
+ .type = BACKLIGHT_RAW,
+ };
+ int ret;
+
+ lcd->backlight = backlight_device_register(lcd->name, &lcd->spi->dev,
+ lcd, &acx565akm_bl_ops,
+ &props);
+ if (IS_ERR(lcd->backlight)) {
+ ret = PTR_ERR(lcd->backlight);
+ lcd->backlight = NULL;
+ return ret;
+ }
+
+ if (lcd->has_cabc) {
+ ret = sysfs_create_group(&lcd->backlight->dev.kobj,
+ &acx565akm_cabc_attr_group);
+ if (ret < 0) {
+ dev_err(&lcd->spi->dev,
+ "%s failed to create sysfs files\n", __func__);
+ backlight_device_unregister(lcd->backlight);
+ return ret;
+ }
+
+ lcd->cabc_mode = acx565akm_get_hw_cabc_mode(lcd);
+ }
+
+ lcd->backlight->props.max_brightness = 255;
+ lcd->backlight->props.brightness = acx565akm_get_actual_brightness(lcd);
+
+ acx565akm_bl_update_status_locked(lcd->backlight);
+
+ return 0;
+}
+
+static void acx565akm_backlight_cleanup(struct acx565akm_panel *lcd)
+{
+ if (lcd->has_cabc)
+ sysfs_remove_group(&lcd->backlight->dev.kobj,
+ &acx565akm_cabc_attr_group);
+
+ backlight_device_unregister(lcd->backlight);
+}
+
+/* -----------------------------------------------------------------------------
+ * DRM Bridge Operations
+ */
+
+static void acx565akm_set_sleep_mode(struct acx565akm_panel *lcd, int on)
+{
+ int cmd = on ? MIPI_DCS_ENTER_SLEEP_MODE : MIPI_DCS_EXIT_SLEEP_MODE;
+ unsigned long wait;
+
+ /*
+ * We have to keep 120msec between sleep in/out commands.
+ * (8.2.15, 8.2.16).
+ */
+ wait = lcd->hw_guard_end - jiffies;
+ if ((long)wait > 0 && wait <= lcd->hw_guard_wait) {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(wait);
+ }
+
+ acx565akm_cmd(lcd, cmd);
+
+ lcd->hw_guard_wait = msecs_to_jiffies(120);
+ lcd->hw_guard_end = jiffies + lcd->hw_guard_wait;
+}
+
+static void acx565akm_set_display_state(struct acx565akm_panel *lcd,
+ int enabled)
+{
+ int cmd = enabled ? MIPI_DCS_SET_DISPLAY_ON : MIPI_DCS_SET_DISPLAY_OFF;
+
+ acx565akm_cmd(lcd, cmd);
+}
+
+static int acx565akm_power_on(struct acx565akm_panel *lcd)
+{
+ /*FIXME tweak me */
+ msleep(50);
+
+ gpiod_set_value(lcd->reset_gpio, 1);
+
+ if (lcd->enabled) {
+ dev_dbg(&lcd->spi->dev, "panel already enabled\n");
+ return 0;
+ }
+
+ /*
+ * We have to meet all the following delay requirements:
+ * 1. tRW: reset pulse width 10usec (7.12.1)
+ * 2. tRT: reset cancel time 5msec (7.12.1)
+ * 3. Providing PCLK,HS,VS signals for 2 frames = ~50msec worst
+ * case (7.6.2)
+ * 4. 120msec before the sleep out command (7.12.1)
+ */
+ msleep(120);
+
+ acx565akm_set_sleep_mode(lcd, 0);
+ lcd->enabled = true;
+
+ /* 5msec between sleep out and the next command. (8.2.16) */
+ usleep_range(5000, 10000);
+ acx565akm_set_display_state(lcd, 1);
+ acx565akm_set_cabc_mode(lcd, lcd->cabc_mode);
+
+ return acx565akm_bl_update_status_locked(lcd->backlight);
+}
+
+static void acx565akm_power_off(struct acx565akm_panel *lcd)
+{
+ if (!lcd->enabled)
+ return;
+
+ acx565akm_set_display_state(lcd, 0);
+ acx565akm_set_sleep_mode(lcd, 1);
+ lcd->enabled = false;
+ /*
+ * We have to provide PCLK,HS,VS signals for 2 frames (worst case
+ * ~50msec) after sending the sleep in command and asserting the
+ * reset signal. We probably could assert the reset w/o the delay
+ * but we still delay to avoid possible artifacts. (7.6.1)
+ */
+ msleep(50);
+
+ gpiod_set_value(lcd->reset_gpio, 0);
+
+ /* FIXME need to tweak this delay */
+ msleep(100);
+}
+
+static int acx565akm_disable(struct drm_panel *panel)
+{
+ struct acx565akm_panel *lcd = to_acx565akm_device(panel);
+
+ mutex_lock(&lcd->mutex);
+ acx565akm_power_off(lcd);
+ mutex_unlock(&lcd->mutex);
+
+ return 0;
+}
+
+static int acx565akm_enable(struct drm_panel *panel)
+{
+ struct acx565akm_panel *lcd = to_acx565akm_device(panel);
+
+ mutex_lock(&lcd->mutex);
+ acx565akm_power_on(lcd);
+ mutex_unlock(&lcd->mutex);
+
+ return 0;
+}
+
+static const struct drm_display_mode acx565akm_mode = {
+ .clock = 24000,
+ .hdisplay = 800,
+ .hsync_start = 800 + 28,
+ .hsync_end = 800 + 28 + 4,
+ .htotal = 800 + 28 + 4 + 24,
+ .vdisplay = 480,
+ .vsync_start = 480 + 3,
+ .vsync_end = 480 + 3 + 3,
+ .vtotal = 480 + 3 + 3 + 4,
+ .vrefresh = 57,
+ .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
+ .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
+ .width_mm = 77,
+ .height_mm = 46,
+};
+
+static int acx565akm_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
+{
+ struct drm_display_mode *mode;
+
+ mode = drm_mode_duplicate(connector->dev, &acx565akm_mode);
+ if (!mode)
+ return -ENOMEM;
+
+ drm_mode_set_name(mode);
+ drm_mode_probed_add(connector, mode);
+
+ connector->display_info.width_mm = acx565akm_mode.width_mm;
+ connector->display_info.height_mm = acx565akm_mode.height_mm;
+ connector->display_info.bus_flags = DRM_BUS_FLAG_DE_HIGH
+ | DRM_BUS_FLAG_SYNC_SAMPLE_POSEDGE
+ | DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE;
+
+ return 1;
+}
+
+static const struct drm_panel_funcs acx565akm_funcs = {
+ .disable = acx565akm_disable,
+ .enable = acx565akm_enable,
+ .get_modes = acx565akm_get_modes,
+};
+
+/* -----------------------------------------------------------------------------
+ * Probe, Detect and Remove
+ */
+
+static int acx565akm_detect(struct acx565akm_panel *lcd)
+{
+ __be32 value;
+ u32 status;
+ int ret = 0;
+
+ /*
+ * After being taken out of reset the panel needs 5ms before the first
+ * command can be sent.
+ */
+ gpiod_set_value(lcd->reset_gpio, 1);
+ usleep_range(5000, 10000);
+
+ acx565akm_read(lcd, MIPI_DCS_GET_DISPLAY_STATUS, (u8 *)&value, 4);
+ status = __be32_to_cpu(value);
+ lcd->enabled = (status & (1 << 17)) && (status & (1 << 10));
+
+ dev_dbg(&lcd->spi->dev,
+ "LCD panel %s by bootloader (status 0x%04x)\n",
+ lcd->enabled ? "enabled" : "disabled ", status);
+
+ acx565akm_read(lcd, MIPI_DCS_GET_DISPLAY_ID, lcd->display_id, 3);
+ dev_dbg(&lcd->spi->dev, "MIPI display ID: %02x%02x%02x\n",
+ lcd->display_id[0], lcd->display_id[1], lcd->display_id[2]);
+
+ switch (lcd->display_id[0]) {
+ case 0x10:
+ lcd->model = MIPID_VER_ACX565AKM;
+ lcd->name = "acx565akm";
+ lcd->has_bc = 1;
+ lcd->has_cabc = 1;
+ break;
+ case 0x29:
+ lcd->model = MIPID_VER_L4F00311;
+ lcd->name = "l4f00311";
+ break;
+ case 0x45:
+ lcd->model = MIPID_VER_LPH8923;
+ lcd->name = "lph8923";
+ break;
+ case 0x83:
+ lcd->model = MIPID_VER_LS041Y3;
+ lcd->name = "ls041y3";
+ break;
+ default:
+ lcd->name = "unknown";
+ dev_err(&lcd->spi->dev, "unknown display ID\n");
+ ret = -ENODEV;
+ goto done;
+ }
+
+ lcd->revision = lcd->display_id[1];
+
+ dev_info(&lcd->spi->dev, "%s rev %02x panel detected\n",
+ lcd->name, lcd->revision);
+
+done:
+ if (!lcd->enabled)
+ gpiod_set_value(lcd->reset_gpio, 0);
+
+ return ret;
+}
+
+static int acx565akm_probe(struct spi_device *spi)
+{
+ struct acx565akm_panel *lcd;
+ int ret;
+
+ lcd = devm_kzalloc(&spi->dev, sizeof(*lcd), GFP_KERNEL);
+ if (!lcd)
+ return -ENOMEM;
+
+ spi_set_drvdata(spi, lcd);
+ spi->mode = SPI_MODE_3;
+
+ lcd->spi = spi;
+ mutex_init(&lcd->mutex);
+
+ lcd->reset_gpio = devm_gpiod_get(&spi->dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(lcd->reset_gpio)) {
+ dev_err(&spi->dev, "failed to get reset GPIO\n");
+ return PTR_ERR(lcd->reset_gpio);
+ }
+
+ ret = acx565akm_detect(lcd);
+ if (ret < 0) {
+ dev_err(&spi->dev, "panel detection failed\n");
+ return ret;
+ }
+
+ if (lcd->has_bc) {
+ ret = acx565akm_backlight_init(lcd);
+ if (ret < 0)
+ return ret;
+ }
+
+ drm_panel_init(&lcd->panel, &lcd->spi->dev, &acx565akm_funcs,
+ DRM_MODE_CONNECTOR_DPI);
+
+ ret = drm_panel_add(&lcd->panel);
+ if (ret < 0) {
+ if (lcd->has_bc)
+ acx565akm_backlight_cleanup(lcd);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int acx565akm_remove(struct spi_device *spi)
+{
+ struct acx565akm_panel *lcd = spi_get_drvdata(spi);
+
+ drm_panel_remove(&lcd->panel);
+
+ if (lcd->has_bc)
+ acx565akm_backlight_cleanup(lcd);
+
+ drm_panel_disable(&lcd->panel);
+ drm_panel_unprepare(&lcd->panel);
+
+ return 0;
+}
+
+static const struct of_device_id acx565akm_of_match[] = {
+ { .compatible = "sony,acx565akm", },
+ { /* sentinel */ },
+};
+
+MODULE_DEVICE_TABLE(of, acx565akm_of_match);
+
+static const struct spi_device_id acx565akm_ids[] = {
+ { "acx565akm", 0 },
+ { /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(spi, acx565akm_ids);
+
+static struct spi_driver acx565akm_driver = {
+ .probe = acx565akm_probe,
+ .remove = acx565akm_remove,
+ .id_table = acx565akm_ids,
+ .driver = {
+ .name = "panel-sony-acx565akm",
+ .of_match_table = acx565akm_of_match,
+ },
+};
+
+module_spi_driver(acx565akm_driver);
+
+MODULE_AUTHOR("Nokia Corporation");
+MODULE_DESCRIPTION("Sony ACX565AKM LCD Panel Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/panel/panel-tpo-td028ttec1.c b/drivers/gpu/drm/panel/panel-tpo-td028ttec1.c
new file mode 100644
index 000000000000..cf29405a2dbe
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-tpo-td028ttec1.c
@@ -0,0 +1,391 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Toppoly TD028TTEC1 Panel Driver
+ *
+ * Copyright (C) 2019 Texas Instruments Incorporated
+ *
+ * Based on the omapdrm-specific panel-tpo-td028ttec1 driver
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
+ *
+ * Neo 1973 code (jbt6k74.c):
+ * Copyright (C) 2006-2007 OpenMoko, Inc.
+ * Author: Harald Welte <laforge@openmoko.org>
+ *
+ * Ported and adapted from Neo 1973 U-Boot by:
+ * H. Nikolaus Schaller <hns@goldelico.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+
+#include <drm/drm_connector.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_panel.h>
+
+#define JBT_COMMAND 0x000
+#define JBT_DATA 0x100
+
+#define JBT_REG_SLEEP_IN 0x10
+#define JBT_REG_SLEEP_OUT 0x11
+
+#define JBT_REG_DISPLAY_OFF 0x28
+#define JBT_REG_DISPLAY_ON 0x29
+
+#define JBT_REG_RGB_FORMAT 0x3a
+#define JBT_REG_QUAD_RATE 0x3b
+
+#define JBT_REG_POWER_ON_OFF 0xb0
+#define JBT_REG_BOOSTER_OP 0xb1
+#define JBT_REG_BOOSTER_MODE 0xb2
+#define JBT_REG_BOOSTER_FREQ 0xb3
+#define JBT_REG_OPAMP_SYSCLK 0xb4
+#define JBT_REG_VSC_VOLTAGE 0xb5
+#define JBT_REG_VCOM_VOLTAGE 0xb6
+#define JBT_REG_EXT_DISPL 0xb7
+#define JBT_REG_OUTPUT_CONTROL 0xb8
+#define JBT_REG_DCCLK_DCEV 0xb9
+#define JBT_REG_DISPLAY_MODE1 0xba
+#define JBT_REG_DISPLAY_MODE2 0xbb
+#define JBT_REG_DISPLAY_MODE 0xbc
+#define JBT_REG_ASW_SLEW 0xbd
+#define JBT_REG_DUMMY_DISPLAY 0xbe
+#define JBT_REG_DRIVE_SYSTEM 0xbf
+
+#define JBT_REG_SLEEP_OUT_FR_A 0xc0
+#define JBT_REG_SLEEP_OUT_FR_B 0xc1
+#define JBT_REG_SLEEP_OUT_FR_C 0xc2
+#define JBT_REG_SLEEP_IN_LCCNT_D 0xc3
+#define JBT_REG_SLEEP_IN_LCCNT_E 0xc4
+#define JBT_REG_SLEEP_IN_LCCNT_F 0xc5
+#define JBT_REG_SLEEP_IN_LCCNT_G 0xc6
+
+#define JBT_REG_GAMMA1_FINE_1 0xc7
+#define JBT_REG_GAMMA1_FINE_2 0xc8
+#define JBT_REG_GAMMA1_INCLINATION 0xc9
+#define JBT_REG_GAMMA1_BLUE_OFFSET 0xca
+
+#define JBT_REG_BLANK_CONTROL 0xcf
+#define JBT_REG_BLANK_TH_TV 0xd0
+#define JBT_REG_CKV_ON_OFF 0xd1
+#define JBT_REG_CKV_1_2 0xd2
+#define JBT_REG_OEV_TIMING 0xd3
+#define JBT_REG_ASW_TIMING_1 0xd4
+#define JBT_REG_ASW_TIMING_2 0xd5
+
+#define JBT_REG_HCLOCK_VGA 0xec
+#define JBT_REG_HCLOCK_QVGA 0xed
+
+struct td028ttec1_panel {
+ struct drm_panel panel;
+
+ struct spi_device *spi;
+};
+
+#define to_td028ttec1_device(p) container_of(p, struct td028ttec1_panel, panel)
+
+static int jbt_ret_write_0(struct td028ttec1_panel *lcd, u8 reg, int *err)
+{
+ struct spi_device *spi = lcd->spi;
+ u16 tx_buf = JBT_COMMAND | reg;
+ int ret;
+
+ if (err && *err)
+ return *err;
+
+ ret = spi_write(spi, (u8 *)&tx_buf, sizeof(tx_buf));
+ if (ret < 0) {
+ dev_err(&spi->dev, "%s: SPI write failed: %d\n", __func__, ret);
+ if (err)
+ *err = ret;
+ }
+
+ return ret;
+}
+
+static int jbt_reg_write_1(struct td028ttec1_panel *lcd,
+ u8 reg, u8 data, int *err)
+{
+ struct spi_device *spi = lcd->spi;
+ u16 tx_buf[2];
+ int ret;
+
+ if (err && *err)
+ return *err;
+
+ tx_buf[0] = JBT_COMMAND | reg;
+ tx_buf[1] = JBT_DATA | data;
+
+ ret = spi_write(spi, (u8 *)tx_buf, sizeof(tx_buf));
+ if (ret < 0) {
+ dev_err(&spi->dev, "%s: SPI write failed: %d\n", __func__, ret);
+ if (err)
+ *err = ret;
+ }
+
+ return ret;
+}
+
+static int jbt_reg_write_2(struct td028ttec1_panel *lcd,
+ u8 reg, u16 data, int *err)
+{
+ struct spi_device *spi = lcd->spi;
+ u16 tx_buf[3];
+ int ret;
+
+ if (err && *err)
+ return *err;
+
+ tx_buf[0] = JBT_COMMAND | reg;
+ tx_buf[1] = JBT_DATA | (data >> 8);
+ tx_buf[2] = JBT_DATA | (data & 0xff);
+
+ ret = spi_write(spi, (u8 *)tx_buf, sizeof(tx_buf));
+ if (ret < 0) {
+ dev_err(&spi->dev, "%s: SPI write failed: %d\n", __func__, ret);
+ if (err)
+ *err = ret;
+ }
+
+ return ret;
+}
+
+static int td028ttec1_prepare(struct drm_panel *panel)
+{
+ struct td028ttec1_panel *lcd = to_td028ttec1_device(panel);
+ unsigned int i;
+ int ret = 0;
+
+ /* Three times command zero */
+ for (i = 0; i < 3; ++i) {
+ jbt_ret_write_0(lcd, 0x00, &ret);
+ usleep_range(1000, 2000);
+ }
+
+ /* deep standby out */
+ jbt_reg_write_1(lcd, JBT_REG_POWER_ON_OFF, 0x17, &ret);
+
+ /* RGB I/F on, RAM write off, QVGA through, SIGCON enable */
+ jbt_reg_write_1(lcd, JBT_REG_DISPLAY_MODE, 0x80, &ret);
+
+ /* Quad mode off */
+ jbt_reg_write_1(lcd, JBT_REG_QUAD_RATE, 0x00, &ret);
+
+ /* AVDD on, XVDD on */
+ jbt_reg_write_1(lcd, JBT_REG_POWER_ON_OFF, 0x16, &ret);
+
+ /* Output control */
+ jbt_reg_write_2(lcd, JBT_REG_OUTPUT_CONTROL, 0xfff9, &ret);
+
+ /* Sleep mode off */
+ jbt_ret_write_0(lcd, JBT_REG_SLEEP_OUT, &ret);
+
+ /* at this point we have like 50% grey */
+
+ /* initialize register set */
+ jbt_reg_write_1(lcd, JBT_REG_DISPLAY_MODE1, 0x01, &ret);
+ jbt_reg_write_1(lcd, JBT_REG_DISPLAY_MODE2, 0x00, &ret);
+ jbt_reg_write_1(lcd, JBT_REG_RGB_FORMAT, 0x60, &ret);
+ jbt_reg_write_1(lcd, JBT_REG_DRIVE_SYSTEM, 0x10, &ret);
+ jbt_reg_write_1(lcd, JBT_REG_BOOSTER_OP, 0x56, &ret);
+ jbt_reg_write_1(lcd, JBT_REG_BOOSTER_MODE, 0x33, &ret);
+ jbt_reg_write_1(lcd, JBT_REG_BOOSTER_FREQ, 0x11, &ret);
+ jbt_reg_write_1(lcd, JBT_REG_BOOSTER_FREQ, 0x11, &ret);
+ jbt_reg_write_1(lcd, JBT_REG_OPAMP_SYSCLK, 0x02, &ret);
+ jbt_reg_write_1(lcd, JBT_REG_VSC_VOLTAGE, 0x2b, &ret);
+ jbt_reg_write_1(lcd, JBT_REG_VCOM_VOLTAGE, 0x40, &ret);
+ jbt_reg_write_1(lcd, JBT_REG_EXT_DISPL, 0x03, &ret);
+ jbt_reg_write_1(lcd, JBT_REG_DCCLK_DCEV, 0x04, &ret);
+ /*
+ * default of 0x02 in JBT_REG_ASW_SLEW responsible for 72Hz requirement
+ * to avoid red / blue flicker
+ */
+ jbt_reg_write_1(lcd, JBT_REG_ASW_SLEW, 0x04, &ret);
+ jbt_reg_write_1(lcd, JBT_REG_DUMMY_DISPLAY, 0x00, &ret);
+
+ jbt_reg_write_1(lcd, JBT_REG_SLEEP_OUT_FR_A, 0x11, &ret);
+ jbt_reg_write_1(lcd, JBT_REG_SLEEP_OUT_FR_B, 0x11, &ret);
+ jbt_reg_write_1(lcd, JBT_REG_SLEEP_OUT_FR_C, 0x11, &ret);
+ jbt_reg_write_2(lcd, JBT_REG_SLEEP_IN_LCCNT_D, 0x2040, &ret);
+ jbt_reg_write_2(lcd, JBT_REG_SLEEP_IN_LCCNT_E, 0x60c0, &ret);
+ jbt_reg_write_2(lcd, JBT_REG_SLEEP_IN_LCCNT_F, 0x1020, &ret);
+ jbt_reg_write_2(lcd, JBT_REG_SLEEP_IN_LCCNT_G, 0x60c0, &ret);
+
+ jbt_reg_write_2(lcd, JBT_REG_GAMMA1_FINE_1, 0x5533, &ret);
+ jbt_reg_write_1(lcd, JBT_REG_GAMMA1_FINE_2, 0x00, &ret);
+ jbt_reg_write_1(lcd, JBT_REG_GAMMA1_INCLINATION, 0x00, &ret);
+ jbt_reg_write_1(lcd, JBT_REG_GAMMA1_BLUE_OFFSET, 0x00, &ret);
+
+ jbt_reg_write_2(lcd, JBT_REG_HCLOCK_VGA, 0x1f0, &ret);
+ jbt_reg_write_1(lcd, JBT_REG_BLANK_CONTROL, 0x02, &ret);
+ jbt_reg_write_2(lcd, JBT_REG_BLANK_TH_TV, 0x0804, &ret);
+
+ jbt_reg_write_1(lcd, JBT_REG_CKV_ON_OFF, 0x01, &ret);
+ jbt_reg_write_2(lcd, JBT_REG_CKV_1_2, 0x0000, &ret);
+
+ jbt_reg_write_2(lcd, JBT_REG_OEV_TIMING, 0x0d0e, &ret);
+ jbt_reg_write_2(lcd, JBT_REG_ASW_TIMING_1, 0x11a4, &ret);
+ jbt_reg_write_1(lcd, JBT_REG_ASW_TIMING_2, 0x0e, &ret);
+
+ return ret;
+}
+
+static int td028ttec1_enable(struct drm_panel *panel)
+{
+ struct td028ttec1_panel *lcd = to_td028ttec1_device(panel);
+ int ret;
+
+ ret = jbt_ret_write_0(lcd, JBT_REG_DISPLAY_ON, NULL);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int td028ttec1_disable(struct drm_panel *panel)
+{
+ struct td028ttec1_panel *lcd = to_td028ttec1_device(panel);
+
+ jbt_ret_write_0(lcd, JBT_REG_DISPLAY_OFF, NULL);
+
+ return 0;
+}
+
+static int td028ttec1_unprepare(struct drm_panel *panel)
+{
+ struct td028ttec1_panel *lcd = to_td028ttec1_device(panel);
+
+ jbt_reg_write_2(lcd, JBT_REG_OUTPUT_CONTROL, 0x8002, NULL);
+ jbt_ret_write_0(lcd, JBT_REG_SLEEP_IN, NULL);
+ jbt_reg_write_1(lcd, JBT_REG_POWER_ON_OFF, 0x00, NULL);
+
+ return 0;
+}
+
+static const struct drm_display_mode td028ttec1_mode = {
+ .clock = 22153,
+ .hdisplay = 480,
+ .hsync_start = 480 + 24,
+ .hsync_end = 480 + 24 + 8,
+ .htotal = 480 + 24 + 8 + 8,
+ .vdisplay = 640,
+ .vsync_start = 640 + 4,
+ .vsync_end = 640 + 4 + 2,
+ .vtotal = 640 + 4 + 2 + 2,
+ .vrefresh = 66,
+ .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
+ .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
+ .width_mm = 43,
+ .height_mm = 58,
+};
+
+static int td028ttec1_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
+{
+ struct drm_display_mode *mode;
+
+ mode = drm_mode_duplicate(connector->dev, &td028ttec1_mode);
+ if (!mode)
+ return -ENOMEM;
+
+ drm_mode_set_name(mode);
+ drm_mode_probed_add(connector, mode);
+
+ connector->display_info.width_mm = td028ttec1_mode.width_mm;
+ connector->display_info.height_mm = td028ttec1_mode.height_mm;
+ /*
+ * FIXME: According to the datasheet sync signals are sampled on the
+ * rising edge of the clock, but the code running on the OpenMoko Neo
+ * FreeRunner and Neo 1973 indicates sampling on the falling edge. This
+ * should be tested on a real device.
+ */
+ connector->display_info.bus_flags = DRM_BUS_FLAG_DE_HIGH
+ | DRM_BUS_FLAG_SYNC_SAMPLE_NEGEDGE
+ | DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE;
+
+ return 1;
+}
+
+static const struct drm_panel_funcs td028ttec1_funcs = {
+ .prepare = td028ttec1_prepare,
+ .enable = td028ttec1_enable,
+ .disable = td028ttec1_disable,
+ .unprepare = td028ttec1_unprepare,
+ .get_modes = td028ttec1_get_modes,
+};
+
+static int td028ttec1_probe(struct spi_device *spi)
+{
+ struct td028ttec1_panel *lcd;
+ int ret;
+
+ lcd = devm_kzalloc(&spi->dev, sizeof(*lcd), GFP_KERNEL);
+ if (!lcd)
+ return -ENOMEM;
+
+ spi_set_drvdata(spi, lcd);
+ lcd->spi = spi;
+
+ spi->mode = SPI_MODE_3;
+ spi->bits_per_word = 9;
+
+ ret = spi_setup(spi);
+ if (ret < 0) {
+ dev_err(&spi->dev, "failed to setup SPI: %d\n", ret);
+ return ret;
+ }
+
+ drm_panel_init(&lcd->panel, &lcd->spi->dev, &td028ttec1_funcs,
+ DRM_MODE_CONNECTOR_DPI);
+
+ ret = drm_panel_of_backlight(&lcd->panel);
+ if (ret)
+ return ret;
+
+ return drm_panel_add(&lcd->panel);
+}
+
+static int td028ttec1_remove(struct spi_device *spi)
+{
+ struct td028ttec1_panel *lcd = spi_get_drvdata(spi);
+
+ drm_panel_remove(&lcd->panel);
+ drm_panel_disable(&lcd->panel);
+ drm_panel_unprepare(&lcd->panel);
+
+ return 0;
+}
+
+static const struct of_device_id td028ttec1_of_match[] = {
+ { .compatible = "tpo,td028ttec1", },
+ /* DT backward compatibility. */
+ { .compatible = "toppoly,td028ttec1", },
+ { /* sentinel */ },
+};
+
+MODULE_DEVICE_TABLE(of, td028ttec1_of_match);
+
+static const struct spi_device_id td028ttec1_ids[] = {
+ { "td028ttec1", 0 },
+ { /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(spi, td028ttec1_ids);
+
+static struct spi_driver td028ttec1_driver = {
+ .probe = td028ttec1_probe,
+ .remove = td028ttec1_remove,
+ .id_table = td028ttec1_ids,
+ .driver = {
+ .name = "panel-tpo-td028ttec1",
+ .of_match_table = td028ttec1_of_match,
+ },
+};
+
+module_spi_driver(td028ttec1_driver);
+
+MODULE_AUTHOR("H. Nikolaus Schaller <hns@goldelico.com>");
+MODULE_DESCRIPTION("Toppoly TD028TTEC1 panel driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/panel/panel-tpo-td043mtea1.c b/drivers/gpu/drm/panel/panel-tpo-td043mtea1.c
new file mode 100644
index 000000000000..75f1f1f1b6de
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-tpo-td043mtea1.c
@@ -0,0 +1,515 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Toppoly TD043MTEA1 Panel Driver
+ *
+ * Copyright (C) 2019 Texas Instruments Incorporated
+ *
+ * Based on the omapdrm-specific panel-tpo-td043mtea1 driver
+ *
+ * Author: Gražvydas Ignotas <notasas@gmail.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+
+#include <drm/drm_connector.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_panel.h>
+
+#define TPO_R02_MODE(x) ((x) & 7)
+#define TPO_R02_MODE_800x480 7
+#define TPO_R02_NCLK_RISING BIT(3)
+#define TPO_R02_HSYNC_HIGH BIT(4)
+#define TPO_R02_VSYNC_HIGH BIT(5)
+
+#define TPO_R03_NSTANDBY BIT(0)
+#define TPO_R03_EN_CP_CLK BIT(1)
+#define TPO_R03_EN_VGL_PUMP BIT(2)
+#define TPO_R03_EN_PWM BIT(3)
+#define TPO_R03_DRIVING_CAP_100 BIT(4)
+#define TPO_R03_EN_PRE_CHARGE BIT(6)
+#define TPO_R03_SOFTWARE_CTL BIT(7)
+
+#define TPO_R04_NFLIP_H BIT(0)
+#define TPO_R04_NFLIP_V BIT(1)
+#define TPO_R04_CP_CLK_FREQ_1H BIT(2)
+#define TPO_R04_VGL_FREQ_1H BIT(4)
+
+#define TPO_R03_VAL_NORMAL \
+ (TPO_R03_NSTANDBY | TPO_R03_EN_CP_CLK | TPO_R03_EN_VGL_PUMP | \
+ TPO_R03_EN_PWM | TPO_R03_DRIVING_CAP_100 | TPO_R03_EN_PRE_CHARGE | \
+ TPO_R03_SOFTWARE_CTL)
+
+#define TPO_R03_VAL_STANDBY \
+ (TPO_R03_DRIVING_CAP_100 | TPO_R03_EN_PRE_CHARGE | \
+ TPO_R03_SOFTWARE_CTL)
+
+static const u16 td043mtea1_def_gamma[12] = {
+ 105, 315, 381, 431, 490, 537, 579, 686, 780, 837, 880, 1023
+};
+
+struct td043mtea1_panel {
+ struct drm_panel panel;
+
+ struct spi_device *spi;
+ struct regulator *vcc_reg;
+ struct gpio_desc *reset_gpio;
+
+ unsigned int mode;
+ u16 gamma[12];
+ bool vmirror;
+ bool powered_on;
+ bool spi_suspended;
+ bool power_on_resume;
+};
+
+#define to_td043mtea1_device(p) container_of(p, struct td043mtea1_panel, panel)
+
+/* -----------------------------------------------------------------------------
+ * Hardware Access
+ */
+
+static int td043mtea1_write(struct td043mtea1_panel *lcd, u8 addr, u8 value)
+{
+ struct spi_message msg;
+ struct spi_transfer xfer;
+ u16 data;
+ int ret;
+
+ spi_message_init(&msg);
+
+ memset(&xfer, 0, sizeof(xfer));
+
+ data = ((u16)addr << 10) | (1 << 8) | value;
+ xfer.tx_buf = &data;
+ xfer.bits_per_word = 16;
+ xfer.len = 2;
+ spi_message_add_tail(&xfer, &msg);
+
+ ret = spi_sync(lcd->spi, &msg);
+ if (ret < 0)
+ dev_warn(&lcd->spi->dev, "failed to write to LCD reg (%d)\n",
+ ret);
+
+ return ret;
+}
+
+static void td043mtea1_write_gamma(struct td043mtea1_panel *lcd)
+{
+ const u16 *gamma = lcd->gamma;
+ unsigned int i;
+ u8 val;
+
+ /* gamma bits [9:8] */
+ for (val = i = 0; i < 4; i++)
+ val |= (gamma[i] & 0x300) >> ((i + 1) * 2);
+ td043mtea1_write(lcd, 0x11, val);
+
+ for (val = i = 0; i < 4; i++)
+ val |= (gamma[i + 4] & 0x300) >> ((i + 1) * 2);
+ td043mtea1_write(lcd, 0x12, val);
+
+ for (val = i = 0; i < 4; i++)
+ val |= (gamma[i + 8] & 0x300) >> ((i + 1) * 2);
+ td043mtea1_write(lcd, 0x13, val);
+
+ /* gamma bits [7:0] */
+ for (i = 0; i < 12; i++)
+ td043mtea1_write(lcd, 0x14 + i, gamma[i] & 0xff);
+}
+
+static int td043mtea1_write_mirror(struct td043mtea1_panel *lcd)
+{
+ u8 reg4 = TPO_R04_NFLIP_H | TPO_R04_NFLIP_V |
+ TPO_R04_CP_CLK_FREQ_1H | TPO_R04_VGL_FREQ_1H;
+ if (lcd->vmirror)
+ reg4 &= ~TPO_R04_NFLIP_V;
+
+ return td043mtea1_write(lcd, 4, reg4);
+}
+
+static int td043mtea1_power_on(struct td043mtea1_panel *lcd)
+{
+ int ret;
+
+ if (lcd->powered_on)
+ return 0;
+
+ ret = regulator_enable(lcd->vcc_reg);
+ if (ret < 0)
+ return ret;
+
+ /* Wait for the panel to stabilize. */
+ msleep(160);
+
+ gpiod_set_value(lcd->reset_gpio, 0);
+
+ td043mtea1_write(lcd, 2, TPO_R02_MODE(lcd->mode) | TPO_R02_NCLK_RISING);
+ td043mtea1_write(lcd, 3, TPO_R03_VAL_NORMAL);
+ td043mtea1_write(lcd, 0x20, 0xf0);
+ td043mtea1_write(lcd, 0x21, 0xf0);
+ td043mtea1_write_mirror(lcd);
+ td043mtea1_write_gamma(lcd);
+
+ lcd->powered_on = true;
+
+ return 0;
+}
+
+static void td043mtea1_power_off(struct td043mtea1_panel *lcd)
+{
+ if (!lcd->powered_on)
+ return;
+
+ td043mtea1_write(lcd, 3, TPO_R03_VAL_STANDBY | TPO_R03_EN_PWM);
+
+ gpiod_set_value(lcd->reset_gpio, 1);
+
+ /* wait for at least 2 vsyncs before cutting off power */
+ msleep(50);
+
+ td043mtea1_write(lcd, 3, TPO_R03_VAL_STANDBY);
+
+ regulator_disable(lcd->vcc_reg);
+
+ lcd->powered_on = false;
+}
+
+/* -----------------------------------------------------------------------------
+ * sysfs
+ */
+
+static ssize_t vmirror_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct td043mtea1_panel *lcd = dev_get_drvdata(dev);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", lcd->vmirror);
+}
+
+static ssize_t vmirror_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct td043mtea1_panel *lcd = dev_get_drvdata(dev);
+ int val;
+ int ret;
+
+ ret = kstrtoint(buf, 0, &val);
+ if (ret < 0)
+ return ret;
+
+ lcd->vmirror = !!val;
+
+ ret = td043mtea1_write_mirror(lcd);
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+
+static ssize_t mode_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct td043mtea1_panel *lcd = dev_get_drvdata(dev);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", lcd->mode);
+}
+
+static ssize_t mode_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct td043mtea1_panel *lcd = dev_get_drvdata(dev);
+ long val;
+ int ret;
+
+ ret = kstrtol(buf, 0, &val);
+ if (ret != 0 || val & ~7)
+ return -EINVAL;
+
+ lcd->mode = val;
+
+ val |= TPO_R02_NCLK_RISING;
+ td043mtea1_write(lcd, 2, val);
+
+ return count;
+}
+
+static ssize_t gamma_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct td043mtea1_panel *lcd = dev_get_drvdata(dev);
+ ssize_t len = 0;
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < ARRAY_SIZE(lcd->gamma); i++) {
+ ret = snprintf(buf + len, PAGE_SIZE - len, "%u ",
+ lcd->gamma[i]);
+ if (ret < 0)
+ return ret;
+ len += ret;
+ }
+ buf[len - 1] = '\n';
+
+ return len;
+}
+
+static ssize_t gamma_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct td043mtea1_panel *lcd = dev_get_drvdata(dev);
+ unsigned int g[12];
+ unsigned int i;
+ int ret;
+
+ ret = sscanf(buf, "%u %u %u %u %u %u %u %u %u %u %u %u",
+ &g[0], &g[1], &g[2], &g[3], &g[4], &g[5],
+ &g[6], &g[7], &g[8], &g[9], &g[10], &g[11]);
+ if (ret != 12)
+ return -EINVAL;
+
+ for (i = 0; i < 12; i++)
+ lcd->gamma[i] = g[i];
+
+ td043mtea1_write_gamma(lcd);
+
+ return count;
+}
+
+static DEVICE_ATTR_RW(vmirror);
+static DEVICE_ATTR_RW(mode);
+static DEVICE_ATTR_RW(gamma);
+
+static struct attribute *td043mtea1_attrs[] = {
+ &dev_attr_vmirror.attr,
+ &dev_attr_mode.attr,
+ &dev_attr_gamma.attr,
+ NULL,
+};
+
+static const struct attribute_group td043mtea1_attr_group = {
+ .attrs = td043mtea1_attrs,
+};
+
+/* -----------------------------------------------------------------------------
+ * Panel Operations
+ */
+
+static int td043mtea1_unprepare(struct drm_panel *panel)
+{
+ struct td043mtea1_panel *lcd = to_td043mtea1_device(panel);
+
+ if (!lcd->spi_suspended)
+ td043mtea1_power_off(lcd);
+
+ return 0;
+}
+
+static int td043mtea1_prepare(struct drm_panel *panel)
+{
+ struct td043mtea1_panel *lcd = to_td043mtea1_device(panel);
+ int ret;
+
+ /*
+ * If we are resuming from system suspend, SPI might not be enabled
+ * yet, so we'll program the LCD from SPI PM resume callback.
+ */
+ if (lcd->spi_suspended)
+ return 0;
+
+ ret = td043mtea1_power_on(lcd);
+ if (ret) {
+ dev_err(&lcd->spi->dev, "%s: power on failed (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct drm_display_mode td043mtea1_mode = {
+ .clock = 36000,
+ .hdisplay = 800,
+ .hsync_start = 800 + 68,
+ .hsync_end = 800 + 68 + 1,
+ .htotal = 800 + 68 + 1 + 214,
+ .vdisplay = 480,
+ .vsync_start = 480 + 39,
+ .vsync_end = 480 + 39 + 1,
+ .vtotal = 480 + 39 + 1 + 34,
+ .vrefresh = 60,
+ .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
+ .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
+ .width_mm = 94,
+ .height_mm = 56,
+};
+
+static int td043mtea1_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
+{
+ struct drm_display_mode *mode;
+
+ mode = drm_mode_duplicate(connector->dev, &td043mtea1_mode);
+ if (!mode)
+ return -ENOMEM;
+
+ drm_mode_set_name(mode);
+ drm_mode_probed_add(connector, mode);
+
+ connector->display_info.width_mm = td043mtea1_mode.width_mm;
+ connector->display_info.height_mm = td043mtea1_mode.height_mm;
+ /*
+ * FIXME: According to the datasheet sync signals are sampled on the
+ * rising edge of the clock, but the code running on the OMAP3 Pandora
+ * indicates sampling on the falling edge. This should be tested on a
+ * real device.
+ */
+ connector->display_info.bus_flags = DRM_BUS_FLAG_DE_HIGH
+ | DRM_BUS_FLAG_SYNC_SAMPLE_NEGEDGE
+ | DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE;
+
+ return 1;
+}
+
+static const struct drm_panel_funcs td043mtea1_funcs = {
+ .unprepare = td043mtea1_unprepare,
+ .prepare = td043mtea1_prepare,
+ .get_modes = td043mtea1_get_modes,
+};
+
+/* -----------------------------------------------------------------------------
+ * Power Management, Probe and Remove
+ */
+
+static int __maybe_unused td043mtea1_suspend(struct device *dev)
+{
+ struct td043mtea1_panel *lcd = dev_get_drvdata(dev);
+
+ if (lcd->powered_on) {
+ td043mtea1_power_off(lcd);
+ lcd->powered_on = true;
+ }
+
+ lcd->spi_suspended = true;
+
+ return 0;
+}
+
+static int __maybe_unused td043mtea1_resume(struct device *dev)
+{
+ struct td043mtea1_panel *lcd = dev_get_drvdata(dev);
+ int ret;
+
+ lcd->spi_suspended = false;
+
+ if (lcd->powered_on) {
+ lcd->powered_on = false;
+ ret = td043mtea1_power_on(lcd);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(td043mtea1_pm_ops, td043mtea1_suspend,
+ td043mtea1_resume);
+
+static int td043mtea1_probe(struct spi_device *spi)
+{
+ struct td043mtea1_panel *lcd;
+ int ret;
+
+ lcd = devm_kzalloc(&spi->dev, sizeof(*lcd), GFP_KERNEL);
+ if (lcd == NULL)
+ return -ENOMEM;
+
+ spi_set_drvdata(spi, lcd);
+ lcd->spi = spi;
+ lcd->mode = TPO_R02_MODE_800x480;
+ memcpy(lcd->gamma, td043mtea1_def_gamma, sizeof(lcd->gamma));
+
+ lcd->vcc_reg = devm_regulator_get(&spi->dev, "vcc");
+ if (IS_ERR(lcd->vcc_reg)) {
+ dev_err(&spi->dev, "failed to get VCC regulator\n");
+ return PTR_ERR(lcd->vcc_reg);
+ }
+
+ lcd->reset_gpio = devm_gpiod_get(&spi->dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(lcd->reset_gpio)) {
+ dev_err(&spi->dev, "failed to get reset GPIO\n");
+ return PTR_ERR(lcd->reset_gpio);
+ }
+
+ spi->bits_per_word = 16;
+ spi->mode = SPI_MODE_0;
+
+ ret = spi_setup(spi);
+ if (ret < 0) {
+ dev_err(&spi->dev, "failed to setup SPI: %d\n", ret);
+ return ret;
+ }
+
+ ret = sysfs_create_group(&spi->dev.kobj, &td043mtea1_attr_group);
+ if (ret < 0) {
+ dev_err(&spi->dev, "failed to create sysfs files\n");
+ return ret;
+ }
+
+ drm_panel_init(&lcd->panel, &lcd->spi->dev, &td043mtea1_funcs,
+ DRM_MODE_CONNECTOR_DPI);
+
+ ret = drm_panel_add(&lcd->panel);
+ if (ret < 0) {
+ sysfs_remove_group(&spi->dev.kobj, &td043mtea1_attr_group);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int td043mtea1_remove(struct spi_device *spi)
+{
+ struct td043mtea1_panel *lcd = spi_get_drvdata(spi);
+
+ drm_panel_remove(&lcd->panel);
+ drm_panel_disable(&lcd->panel);
+ drm_panel_unprepare(&lcd->panel);
+
+ sysfs_remove_group(&spi->dev.kobj, &td043mtea1_attr_group);
+
+ return 0;
+}
+
+static const struct of_device_id td043mtea1_of_match[] = {
+ { .compatible = "tpo,td043mtea1", },
+ { /* sentinel */ },
+};
+
+MODULE_DEVICE_TABLE(of, td043mtea1_of_match);
+
+static const struct spi_device_id td043mtea1_ids[] = {
+ { "td043mtea1", 0 },
+ { /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(spi, td043mtea1_ids);
+
+static struct spi_driver td043mtea1_driver = {
+ .probe = td043mtea1_probe,
+ .remove = td043mtea1_remove,
+ .id_table = td043mtea1_ids,
+ .driver = {
+ .name = "panel-tpo-td043mtea1",
+ .pm = &td043mtea1_pm_ops,
+ .of_match_table = td043mtea1_of_match,
+ },
+};
+
+module_spi_driver(td043mtea1_driver);
+
+MODULE_AUTHOR("Gražvydas Ignotas <notasas@gmail.com>");
+MODULE_DESCRIPTION("TPO TD043MTEA1 Panel Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/panel/panel-tpo-tpg110.c b/drivers/gpu/drm/panel/panel-tpo-tpg110.c
index 71591e5f5938..8472d018c16f 100644
--- a/drivers/gpu/drm/panel/panel-tpo-tpg110.c
+++ b/drivers/gpu/drm/panel/panel-tpo-tpg110.c
@@ -14,13 +14,13 @@
#include <drm/drm_panel.h>
#include <drm/drm_print.h>
-#include <linux/backlight.h>
#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
@@ -77,10 +77,6 @@ struct tpg110 {
*/
struct drm_panel panel;
/**
- * @backlight: backlight for this panel
- */
- struct backlight_device *backlight;
- /**
* @panel_type: the panel mode as detected
*/
const struct tpg110_panel_mode *panel_mode;
@@ -356,8 +352,6 @@ static int tpg110_disable(struct drm_panel *panel)
val &= ~TPG110_CTRL2_PM;
tpg110_write_reg(tpg, TPG110_CTRL2_PM, val);
- backlight_disable(tpg->backlight);
-
return 0;
}
@@ -366,8 +360,6 @@ static int tpg110_enable(struct drm_panel *panel)
struct tpg110 *tpg = to_tpg110(panel);
u8 val;
- backlight_enable(tpg->backlight);
-
/* Take chip out of standby */
val = tpg110_read_reg(tpg, TPG110_CTRL2_PM);
val |= TPG110_CTRL2_PM;
@@ -384,9 +376,9 @@ static int tpg110_enable(struct drm_panel *panel)
* presents the mode that is configured for the system under use,
* and which is detected by reading the registers of the display.
*/
-static int tpg110_get_modes(struct drm_panel *panel)
+static int tpg110_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
{
- struct drm_connector *connector = panel->connector;
struct tpg110 *tpg = to_tpg110(panel);
struct drm_display_mode *mode;
@@ -394,7 +386,7 @@ static int tpg110_get_modes(struct drm_panel *panel)
connector->display_info.height_mm = tpg->height;
connector->display_info.bus_flags = tpg->panel_mode->bus_flags;
- mode = drm_mode_duplicate(panel->drm, &tpg->panel_mode->mode);
+ mode = drm_mode_duplicate(connector->dev, &tpg->panel_mode->mode);
drm_mode_set_name(mode);
mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
@@ -432,11 +424,6 @@ static int tpg110_probe(struct spi_device *spi)
if (ret)
DRM_DEV_ERROR(dev, "no panel height specified\n");
- /* Look for some optional backlight */
- tpg->backlight = devm_of_find_backlight(dev);
- if (IS_ERR(tpg->backlight))
- return PTR_ERR(tpg->backlight);
-
/* This asserts the GRESTB signal, putting the display into reset */
tpg->grestb = devm_gpiod_get(dev, "grestb", GPIOD_OUT_HIGH);
if (IS_ERR(tpg->grestb)) {
@@ -457,9 +444,13 @@ static int tpg110_probe(struct spi_device *spi)
if (ret)
return ret;
- drm_panel_init(&tpg->panel);
- tpg->panel.dev = dev;
- tpg->panel.funcs = &tpg110_drm_funcs;
+ drm_panel_init(&tpg->panel, dev, &tpg110_drm_funcs,
+ DRM_MODE_CONNECTOR_DPI);
+
+ ret = drm_panel_of_backlight(&tpg->panel);
+ if (ret)
+ return ret;
+
spi_set_drvdata(spi, tpg);
return drm_panel_add(&tpg->panel);
diff --git a/drivers/gpu/drm/panel/panel-truly-nt35597.c b/drivers/gpu/drm/panel/panel-truly-nt35597.c
index 77e1311b7c69..012ca62bf30e 100644
--- a/drivers/gpu/drm/panel/panel-truly-nt35597.c
+++ b/drivers/gpu/drm/panel/panel-truly-nt35597.c
@@ -454,9 +454,9 @@ static int truly_nt35597_enable(struct drm_panel *panel)
return 0;
}
-static int truly_nt35597_get_modes(struct drm_panel *panel)
+static int truly_nt35597_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
{
- struct drm_connector *connector = panel->connector;
struct truly_nt35597 *ctx = panel_to_ctx(panel);
struct drm_display_mode *mode;
const struct nt35597_config *config;
@@ -518,9 +518,8 @@ static int truly_nt35597_panel_add(struct truly_nt35597 *ctx)
/* dual port */
gpiod_set_value(ctx->mode_gpio, 0);
- drm_panel_init(&ctx->panel);
- ctx->panel.dev = dev;
- ctx->panel.funcs = &truly_nt35597_drm_funcs;
+ drm_panel_init(&ctx->panel, dev, &truly_nt35597_drm_funcs,
+ DRM_MODE_CONNECTOR_DSI);
drm_panel_add(&ctx->panel);
return 0;
diff --git a/drivers/gpu/drm/panel/panel-xinpeng-xpp055c272.c b/drivers/gpu/drm/panel/panel-xinpeng-xpp055c272.c
new file mode 100644
index 000000000000..1645aceab597
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-xinpeng-xpp055c272.c
@@ -0,0 +1,398 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Xinpeng xpp055c272 5.5" MIPI-DSI panel driver
+ * Copyright (C) 2019 Theobroma Systems Design und Consulting GmbH
+ *
+ * based on
+ *
+ * Rockteck jh057n00900 5.5" MIPI-DSI panel driver
+ * Copyright (C) Purism SPC 2019
+ */
+
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_print.h>
+
+#include <video/display_timing.h>
+#include <video/mipi_display.h>
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/media-bus-format.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
+
+/* Manufacturer specific Commands send via DSI */
+#define XPP055C272_CMD_ALL_PIXEL_OFF 0x22
+#define XPP055C272_CMD_ALL_PIXEL_ON 0x23
+#define XPP055C272_CMD_SETDISP 0xb2
+#define XPP055C272_CMD_SETRGBIF 0xb3
+#define XPP055C272_CMD_SETCYC 0xb4
+#define XPP055C272_CMD_SETBGP 0xb5
+#define XPP055C272_CMD_SETVCOM 0xb6
+#define XPP055C272_CMD_SETOTP 0xb7
+#define XPP055C272_CMD_SETPOWER_EXT 0xb8
+#define XPP055C272_CMD_SETEXTC 0xb9
+#define XPP055C272_CMD_SETMIPI 0xbA
+#define XPP055C272_CMD_SETVDC 0xbc
+#define XPP055C272_CMD_SETPCR 0xbf
+#define XPP055C272_CMD_SETSCR 0xc0
+#define XPP055C272_CMD_SETPOWER 0xc1
+#define XPP055C272_CMD_SETECO 0xc6
+#define XPP055C272_CMD_SETPANEL 0xcc
+#define XPP055C272_CMD_SETGAMMA 0xe0
+#define XPP055C272_CMD_SETEQ 0xe3
+#define XPP055C272_CMD_SETGIP1 0xe9
+#define XPP055C272_CMD_SETGIP2 0xea
+
+struct xpp055c272 {
+ struct device *dev;
+ struct drm_panel panel;
+ struct gpio_desc *reset_gpio;
+ struct regulator *vci;
+ struct regulator *iovcc;
+ bool prepared;
+};
+
+static inline struct xpp055c272 *panel_to_xpp055c272(struct drm_panel *panel)
+{
+ return container_of(panel, struct xpp055c272, panel);
+}
+
+#define dsi_generic_write_seq(dsi, cmd, seq...) do { \
+ static const u8 d[] = { seq }; \
+ int ret; \
+ ret = mipi_dsi_dcs_write(dsi, cmd, d, ARRAY_SIZE(d)); \
+ if (ret < 0) \
+ return ret; \
+ } while (0)
+
+static int xpp055c272_init_sequence(struct xpp055c272 *ctx)
+{
+ struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
+ struct device *dev = ctx->dev;
+
+ /*
+ * Init sequence was supplied by the panel vendor without much
+ * documentation.
+ */
+ dsi_generic_write_seq(dsi, XPP055C272_CMD_SETEXTC, 0xf1, 0x12, 0x83);
+ dsi_generic_write_seq(dsi, XPP055C272_CMD_SETMIPI,
+ 0x33, 0x81, 0x05, 0xf9, 0x0e, 0x0e, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x25,
+ 0x00, 0x91, 0x0a, 0x00, 0x00, 0x02, 0x4f, 0x01,
+ 0x00, 0x00, 0x37);
+ dsi_generic_write_seq(dsi, XPP055C272_CMD_SETPOWER_EXT, 0x25);
+ dsi_generic_write_seq(dsi, XPP055C272_CMD_SETPCR, 0x02, 0x11, 0x00);
+ dsi_generic_write_seq(dsi, XPP055C272_CMD_SETRGBIF,
+ 0x0c, 0x10, 0x0a, 0x50, 0x03, 0xff, 0x00, 0x00,
+ 0x00, 0x00);
+ dsi_generic_write_seq(dsi, XPP055C272_CMD_SETSCR,
+ 0x73, 0x73, 0x50, 0x50, 0x00, 0x00, 0x08, 0x70,
+ 0x00);
+ dsi_generic_write_seq(dsi, XPP055C272_CMD_SETVDC, 0x46);
+ dsi_generic_write_seq(dsi, XPP055C272_CMD_SETPANEL, 0x0b);
+ dsi_generic_write_seq(dsi, XPP055C272_CMD_SETCYC, 0x80);
+ dsi_generic_write_seq(dsi, XPP055C272_CMD_SETDISP, 0xc8, 0x12, 0x30);
+ dsi_generic_write_seq(dsi, XPP055C272_CMD_SETEQ,
+ 0x07, 0x07, 0x0B, 0x0B, 0x03, 0x0B, 0x00, 0x00,
+ 0x00, 0x00, 0xFF, 0x00, 0xC0, 0x10);
+ dsi_generic_write_seq(dsi, XPP055C272_CMD_SETPOWER,
+ 0x53, 0x00, 0x1e, 0x1e, 0x77, 0xe1, 0xcc, 0xdd,
+ 0x67, 0x77, 0x33, 0x33);
+ dsi_generic_write_seq(dsi, XPP055C272_CMD_SETECO, 0x00, 0x00, 0xff,
+ 0xff, 0x01, 0xff);
+ dsi_generic_write_seq(dsi, XPP055C272_CMD_SETBGP, 0x09, 0x09);
+ msleep(20);
+
+ dsi_generic_write_seq(dsi, XPP055C272_CMD_SETVCOM, 0x87, 0x95);
+ dsi_generic_write_seq(dsi, XPP055C272_CMD_SETGIP1,
+ 0xc2, 0x10, 0x05, 0x05, 0x10, 0x05, 0xa0, 0x12,
+ 0x31, 0x23, 0x3f, 0x81, 0x0a, 0xa0, 0x37, 0x18,
+ 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x01, 0x00, 0x00, 0x00, 0x48, 0xf8, 0x86, 0x42,
+ 0x08, 0x88, 0x88, 0x80, 0x88, 0x88, 0x88, 0x58,
+ 0xf8, 0x87, 0x53, 0x18, 0x88, 0x88, 0x81, 0x88,
+ 0x88, 0x88, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
+ dsi_generic_write_seq(dsi, XPP055C272_CMD_SETGIP2,
+ 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x1f, 0x88, 0x81, 0x35,
+ 0x78, 0x88, 0x88, 0x85, 0x88, 0x88, 0x88, 0x0f,
+ 0x88, 0x80, 0x24, 0x68, 0x88, 0x88, 0x84, 0x88,
+ 0x88, 0x88, 0x23, 0x10, 0x00, 0x00, 0x1c, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x05,
+ 0xa0, 0x00, 0x00, 0x00, 0x00);
+ dsi_generic_write_seq(dsi, XPP055C272_CMD_SETGAMMA,
+ 0x00, 0x06, 0x08, 0x2a, 0x31, 0x3f, 0x38, 0x36,
+ 0x07, 0x0c, 0x0d, 0x11, 0x13, 0x12, 0x13, 0x11,
+ 0x18, 0x00, 0x06, 0x08, 0x2a, 0x31, 0x3f, 0x38,
+ 0x36, 0x07, 0x0c, 0x0d, 0x11, 0x13, 0x12, 0x13,
+ 0x11, 0x18);
+
+ msleep(60);
+
+ DRM_DEV_DEBUG_DRIVER(dev, "Panel init sequence done\n");
+ return 0;
+}
+
+static int xpp055c272_unprepare(struct drm_panel *panel)
+{
+ struct xpp055c272 *ctx = panel_to_xpp055c272(panel);
+ struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
+ int ret;
+
+ if (!ctx->prepared)
+ return 0;
+
+ ret = mipi_dsi_dcs_set_display_off(dsi);
+ if (ret < 0)
+ DRM_DEV_ERROR(ctx->dev, "failed to set display off: %d\n",
+ ret);
+
+ mipi_dsi_dcs_enter_sleep_mode(dsi);
+ if (ret < 0) {
+ DRM_DEV_ERROR(ctx->dev, "failed to enter sleep mode: %d\n",
+ ret);
+ return ret;
+ }
+
+ regulator_disable(ctx->iovcc);
+ regulator_disable(ctx->vci);
+
+ ctx->prepared = false;
+
+ return 0;
+}
+
+static int xpp055c272_prepare(struct drm_panel *panel)
+{
+ struct xpp055c272 *ctx = panel_to_xpp055c272(panel);
+ struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
+ int ret;
+
+ if (ctx->prepared)
+ return 0;
+
+ DRM_DEV_DEBUG_DRIVER(ctx->dev, "Resetting the panel\n");
+ ret = regulator_enable(ctx->vci);
+ if (ret < 0) {
+ DRM_DEV_ERROR(ctx->dev,
+ "Failed to enable vci 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_vci;
+ }
+
+ gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+ /* T6: 10us */
+ usleep_range(10, 20);
+ gpiod_set_value_cansleep(ctx->reset_gpio, 0);
+
+ /* T8: 20ms */
+ msleep(20);
+
+ ret = xpp055c272_init_sequence(ctx);
+ if (ret < 0) {
+ DRM_DEV_ERROR(ctx->dev, "Panel init sequence failed: %d\n",
+ ret);
+ goto disable_iovcc;
+ }
+
+ ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
+ if (ret < 0) {
+ DRM_DEV_ERROR(ctx->dev, "Failed to exit sleep mode: %d\n", ret);
+ goto disable_iovcc;
+ }
+
+ /* T9: 120ms */
+ msleep(120);
+
+ ret = mipi_dsi_dcs_set_display_on(dsi);
+ if (ret < 0) {
+ DRM_DEV_ERROR(ctx->dev, "Failed to set display on: %d\n", ret);
+ goto disable_iovcc;
+ }
+
+ msleep(50);
+
+ ctx->prepared = true;
+
+ return 0;
+
+disable_iovcc:
+ regulator_disable(ctx->iovcc);
+disable_vci:
+ regulator_disable(ctx->vci);
+ return ret;
+}
+
+static const struct drm_display_mode default_mode = {
+ .hdisplay = 720,
+ .hsync_start = 720 + 40,
+ .hsync_end = 720 + 40 + 10,
+ .htotal = 720 + 40 + 10 + 40,
+ .vdisplay = 1280,
+ .vsync_start = 1280 + 22,
+ .vsync_end = 1280 + 22 + 4,
+ .vtotal = 1280 + 22 + 4 + 11,
+ .vrefresh = 60,
+ .clock = 64000,
+ .width_mm = 68,
+ .height_mm = 121,
+};
+
+static int xpp055c272_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
+{
+ struct xpp055c272 *ctx = panel_to_xpp055c272(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 xpp055c272_funcs = {
+ .unprepare = xpp055c272_unprepare,
+ .prepare = xpp055c272_prepare,
+ .get_modes = xpp055c272_get_modes,
+};
+
+static int xpp055c272_probe(struct mipi_dsi_device *dsi)
+{
+ struct device *dev = &dsi->dev;
+ struct xpp055c272 *ctx;
+ int ret;
+
+ ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->reset_gpio = devm_gpiod_get_optional(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);
+ }
+
+ ctx->vci = devm_regulator_get(dev, "vci");
+ if (IS_ERR(ctx->vci)) {
+ ret = PTR_ERR(ctx->vci);
+ if (ret != -EPROBE_DEFER)
+ DRM_DEV_ERROR(dev,
+ "Failed to request vci 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;
+ }
+
+ 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_LPM | MIPI_DSI_MODE_EOT_PACKET;
+
+ drm_panel_init(&ctx->panel, &dsi->dev, &xpp055c272_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\n", ret);
+ drm_panel_remove(&ctx->panel);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void xpp055c272_shutdown(struct mipi_dsi_device *dsi)
+{
+ struct xpp055c272 *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 xpp055c272_remove(struct mipi_dsi_device *dsi)
+{
+ struct xpp055c272 *ctx = mipi_dsi_get_drvdata(dsi);
+ int ret;
+
+ xpp055c272_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);
+
+ return 0;
+}
+
+static const struct of_device_id xpp055c272_of_match[] = {
+ { .compatible = "xinpeng,xpp055c272" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, xpp055c272_of_match);
+
+static struct mipi_dsi_driver xpp055c272_driver = {
+ .driver = {
+ .name = "panel-xinpeng-xpp055c272",
+ .of_match_table = xpp055c272_of_match,
+ },
+ .probe = xpp055c272_probe,
+ .remove = xpp055c272_remove,
+ .shutdown = xpp055c272_shutdown,
+};
+module_mipi_dsi_driver(xpp055c272_driver);
+
+MODULE_AUTHOR("Heiko Stuebner <heiko.stuebner@theobroma-systems.com>");
+MODULE_DESCRIPTION("DRM driver for Xinpeng xpp055c272 MIPI DSI panel");
+MODULE_LICENSE("GPL v2");
OpenPOWER on IntegriCloud