diff options
Diffstat (limited to 'drivers/gpu/drm/panel')
-rw-r--r-- | drivers/gpu/drm/panel/Kconfig | 13 | ||||
-rw-r--r-- | drivers/gpu/drm/panel/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/panel/panel-edp.c | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/panel/panel-ilitek-ili9881c.c | 194 | ||||
-rw-r--r-- | drivers/gpu/drm/panel/panel-simple.c | 54 | ||||
-rw-r--r-- | drivers/gpu/drm/panel/panel-sitronix-st7789v.c | 262 | ||||
-rw-r--r-- | drivers/gpu/drm/panel/panel-startek-kd070fhfid015.c | 406 |
7 files changed, 886 insertions, 45 deletions
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index 05f9e800e448..869e535faefa 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -734,6 +734,17 @@ config DRM_PANEL_SONY_TULIP_TRULY_NT35521 NT35521 1280x720 video mode panel as found on Sony Xperia M4 Aqua phone. +config DRM_PANEL_STARTEK_KD070FHFID015 + tristate "STARTEK KD070FHFID015 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 STARTEK KD070FHFID015 DSI panel + based on RENESAS-R69429 controller. The pannel is a 7-inch TFT LCD display + with a resolution of 1024 x 600 pixels. It provides a MIPI DSI interface to + the host, a built-in LED backlight and touch controller. + config DRM_PANEL_TDO_TL070WSH30 tristate "TDO TL070WSH30 DSI panel" depends on OF @@ -799,6 +810,8 @@ config DRM_PANEL_VISIONOX_R66451 depends on OF depends on DRM_MIPI_DSI depends on BACKLIGHT_CLASS_DEVICE + select DRM_DISPLAY_DP_HELPER + select DRM_DISPLAY_HELPER help Say Y here if you want to enable support for Visionox R66451 1080x2340 AMOLED DSI panel. diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile index 499e38244253..433e93d57949 100644 --- a/drivers/gpu/drm/panel/Makefile +++ b/drivers/gpu/drm/panel/Makefile @@ -74,6 +74,7 @@ obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7789V) += panel-sitronix-st7789v.o obj-$(CONFIG_DRM_PANEL_SONY_ACX565AKM) += panel-sony-acx565akm.o obj-$(CONFIG_DRM_PANEL_SONY_TD4353_JDI) += panel-sony-td4353-jdi.o obj-$(CONFIG_DRM_PANEL_SONY_TULIP_TRULY_NT35521) += panel-sony-tulip-truly-nt35521.o +obj-$(CONFIG_DRM_PANEL_STARTEK_KD070FHFID015) += panel-startek-kd070fhfid015.o obj-$(CONFIG_DRM_PANEL_TDO_TL070WSH30) += panel-tdo-tl070wsh30.o obj-$(CONFIG_DRM_PANEL_TPO_TD028TTEC1) += panel-tpo-td028ttec1.o obj-$(CONFIG_DRM_PANEL_TPO_TD043MTEA1) += panel-tpo-td043mtea1.o diff --git a/drivers/gpu/drm/panel/panel-edp.c b/drivers/gpu/drm/panel/panel-edp.c index df7e3cff004c..feb665df35a1 100644 --- a/drivers/gpu/drm/panel/panel-edp.c +++ b/drivers/gpu/drm/panel/panel-edp.c @@ -1890,6 +1890,7 @@ static const struct edp_panel_entry edp_panels[] = { EDP_PANEL_ENTRY('C', 'M', 'N', 0x1153, &delay_200_500_e80_d50, "N116BGE-EA2"), EDP_PANEL_ENTRY('C', 'M', 'N', 0x1154, &delay_200_500_e80_d50, "N116BCA-EA2"), EDP_PANEL_ENTRY('C', 'M', 'N', 0x1247, &delay_200_500_e80_d50, "N120ACA-EA1"), + EDP_PANEL_ENTRY('C', 'M', 'N', 0x14d4, &delay_200_500_e80_d50, "N140HCA-EAC"), EDP_PANEL_ENTRY('I', 'V', 'O', 0x057d, &delay_200_500_e200, "R140NWF5 RH"), EDP_PANEL_ENTRY('I', 'V', 'O', 0x854a, &delay_200_500_p2e100, "M133NW4J"), diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c b/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c index 72dbb8184280..7838947a1bf3 100644 --- a/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c +++ b/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c @@ -455,6 +455,174 @@ static const struct ili9881c_instr k101_im2byl02_init[] = { ILI9881C_COMMAND_INSTR(0xD3, 0x3F), /* VN0 */ }; +static const struct ili9881c_instr tl050hdv35_init[] = { + ILI9881C_SWITCH_PAGE_INSTR(3), + ILI9881C_COMMAND_INSTR(0x01, 0x00), + ILI9881C_COMMAND_INSTR(0x02, 0x00), + ILI9881C_COMMAND_INSTR(0x03, 0x73), + ILI9881C_COMMAND_INSTR(0x04, 0x00), + ILI9881C_COMMAND_INSTR(0x05, 0x00), + ILI9881C_COMMAND_INSTR(0x06, 0x0a), + ILI9881C_COMMAND_INSTR(0x07, 0x00), + ILI9881C_COMMAND_INSTR(0x08, 0x00), + ILI9881C_COMMAND_INSTR(0x09, 0x01), + ILI9881C_COMMAND_INSTR(0x0a, 0x00), + ILI9881C_COMMAND_INSTR(0x0b, 0x00), + ILI9881C_COMMAND_INSTR(0x0c, 0x01), + ILI9881C_COMMAND_INSTR(0x0d, 0x00), + ILI9881C_COMMAND_INSTR(0x0e, 0x00), + ILI9881C_COMMAND_INSTR(0x0f, 0x1d), + ILI9881C_COMMAND_INSTR(0x10, 0x1d), + ILI9881C_COMMAND_INSTR(0x15, 0x00), + ILI9881C_COMMAND_INSTR(0x16, 0x00), + ILI9881C_COMMAND_INSTR(0x17, 0x00), + ILI9881C_COMMAND_INSTR(0x18, 0x00), + ILI9881C_COMMAND_INSTR(0x19, 0x00), + ILI9881C_COMMAND_INSTR(0x1a, 0x00), + ILI9881C_COMMAND_INSTR(0x1b, 0x00), + ILI9881C_COMMAND_INSTR(0x1c, 0x00), + ILI9881C_COMMAND_INSTR(0x1d, 0x00), + ILI9881C_COMMAND_INSTR(0x1e, 0x40), + ILI9881C_COMMAND_INSTR(0x1f, 0x80), + ILI9881C_COMMAND_INSTR(0x20, 0x06), + ILI9881C_COMMAND_INSTR(0x21, 0x02), + ILI9881C_COMMAND_INSTR(0x28, 0x33), + ILI9881C_COMMAND_INSTR(0x29, 0x03), + ILI9881C_COMMAND_INSTR(0x2a, 0x00), + ILI9881C_COMMAND_INSTR(0x2b, 0x00), + ILI9881C_COMMAND_INSTR(0x2c, 0x00), + ILI9881C_COMMAND_INSTR(0x2d, 0x00), + ILI9881C_COMMAND_INSTR(0x2e, 0x00), + ILI9881C_COMMAND_INSTR(0x2f, 0x00), + ILI9881C_COMMAND_INSTR(0x35, 0x00), + ILI9881C_COMMAND_INSTR(0x36, 0x00), + ILI9881C_COMMAND_INSTR(0x37, 0x00), + ILI9881C_COMMAND_INSTR(0x38, 0x3C), + ILI9881C_COMMAND_INSTR(0x39, 0x00), + ILI9881C_COMMAND_INSTR(0x3a, 0x40), + ILI9881C_COMMAND_INSTR(0x3b, 0x40), + ILI9881C_COMMAND_INSTR(0x3c, 0x00), + ILI9881C_COMMAND_INSTR(0x3d, 0x00), + ILI9881C_COMMAND_INSTR(0x3e, 0x00), + ILI9881C_COMMAND_INSTR(0x3f, 0x00), + ILI9881C_COMMAND_INSTR(0x40, 0x00), + ILI9881C_COMMAND_INSTR(0x41, 0x00), + ILI9881C_COMMAND_INSTR(0x42, 0x00), + ILI9881C_COMMAND_INSTR(0x43, 0x00), + ILI9881C_COMMAND_INSTR(0x44, 0x00), + ILI9881C_COMMAND_INSTR(0x55, 0xab), + ILI9881C_COMMAND_INSTR(0x5a, 0x89), + ILI9881C_COMMAND_INSTR(0x5b, 0xab), + ILI9881C_COMMAND_INSTR(0x5c, 0xcd), + ILI9881C_COMMAND_INSTR(0x5d, 0xef), + ILI9881C_COMMAND_INSTR(0x5e, 0x11), + ILI9881C_COMMAND_INSTR(0x5f, 0x01), + ILI9881C_COMMAND_INSTR(0x60, 0x00), + ILI9881C_COMMAND_INSTR(0x61, 0x15), + ILI9881C_COMMAND_INSTR(0x62, 0x14), + ILI9881C_COMMAND_INSTR(0x63, 0x0e), + ILI9881C_COMMAND_INSTR(0x64, 0x0f), + ILI9881C_COMMAND_INSTR(0x65, 0x0c), + ILI9881C_COMMAND_INSTR(0x66, 0x0d), + ILI9881C_COMMAND_INSTR(0x67, 0x06), + ILI9881C_COMMAND_INSTR(0x68, 0x02), + ILI9881C_COMMAND_INSTR(0x69, 0x07), + ILI9881C_COMMAND_INSTR(0x6a, 0x02), + ILI9881C_COMMAND_INSTR(0x6b, 0x02), + ILI9881C_COMMAND_INSTR(0x6c, 0x02), + ILI9881C_COMMAND_INSTR(0x6d, 0x02), + ILI9881C_COMMAND_INSTR(0x6e, 0x02), + ILI9881C_COMMAND_INSTR(0x6f, 0x02), + ILI9881C_COMMAND_INSTR(0x70, 0x02), + ILI9881C_COMMAND_INSTR(0x71, 0x02), + ILI9881C_COMMAND_INSTR(0x72, 0x02), + ILI9881C_COMMAND_INSTR(0x73, 0x02), + ILI9881C_COMMAND_INSTR(0x74, 0x02), + ILI9881C_COMMAND_INSTR(0x75, 0x01), + ILI9881C_COMMAND_INSTR(0x76, 0x00), + ILI9881C_COMMAND_INSTR(0x77, 0x14), + ILI9881C_COMMAND_INSTR(0x78, 0x15), + ILI9881C_COMMAND_INSTR(0x79, 0x0e), + ILI9881C_COMMAND_INSTR(0x7a, 0x0f), + ILI9881C_COMMAND_INSTR(0x7b, 0x0c), + ILI9881C_COMMAND_INSTR(0x7c, 0x0d), + ILI9881C_COMMAND_INSTR(0x7d, 0x06), + ILI9881C_COMMAND_INSTR(0x7e, 0x02), + ILI9881C_COMMAND_INSTR(0x7f, 0x07), + ILI9881C_COMMAND_INSTR(0x88, 0x02), + ILI9881C_COMMAND_INSTR(0x89, 0x02), + ILI9881C_COMMAND_INSTR(0x8A, 0x02), + ILI9881C_SWITCH_PAGE_INSTR(4), + ILI9881C_COMMAND_INSTR(0x38, 0x01), + ILI9881C_COMMAND_INSTR(0x39, 0x00), + ILI9881C_COMMAND_INSTR(0x6c, 0x15), + ILI9881C_COMMAND_INSTR(0x6e, 0x2b), + ILI9881C_COMMAND_INSTR(0x6f, 0x33), + ILI9881C_COMMAND_INSTR(0x8d, 0x18), + ILI9881C_COMMAND_INSTR(0x87, 0xba), + ILI9881C_COMMAND_INSTR(0x26, 0x76), + ILI9881C_COMMAND_INSTR(0xb2, 0xd1), + ILI9881C_COMMAND_INSTR(0xb5, 0x06), + ILI9881C_COMMAND_INSTR(0x3a, 0x24), + ILI9881C_COMMAND_INSTR(0x35, 0x1f), + ILI9881C_COMMAND_INSTR(0x33, 0x14), + ILI9881C_COMMAND_INSTR(0x3b, 0x98), + ILI9881C_SWITCH_PAGE_INSTR(1), + ILI9881C_COMMAND_INSTR(0x22, 0x0a), + ILI9881C_COMMAND_INSTR(0x31, 0x00), + ILI9881C_COMMAND_INSTR(0x40, 0x33), + ILI9881C_COMMAND_INSTR(0x53, 0xa2), + ILI9881C_COMMAND_INSTR(0x55, 0x92), + ILI9881C_COMMAND_INSTR(0x50, 0x96), + ILI9881C_COMMAND_INSTR(0x51, 0x96), + ILI9881C_COMMAND_INSTR(0x60, 0x22), + ILI9881C_COMMAND_INSTR(0x61, 0x00), + ILI9881C_COMMAND_INSTR(0x62, 0x19), + ILI9881C_COMMAND_INSTR(0x63, 0x00), + ILI9881C_COMMAND_INSTR(0xa0, 0x08), + ILI9881C_COMMAND_INSTR(0xa1, 0x11), + ILI9881C_COMMAND_INSTR(0xa2, 0x19), + ILI9881C_COMMAND_INSTR(0xa3, 0x0d), + ILI9881C_COMMAND_INSTR(0xa4, 0x0d), + ILI9881C_COMMAND_INSTR(0xa5, 0x1e), + ILI9881C_COMMAND_INSTR(0xa6, 0x14), + ILI9881C_COMMAND_INSTR(0xa7, 0x17), + ILI9881C_COMMAND_INSTR(0xa8, 0x4f), + ILI9881C_COMMAND_INSTR(0xa9, 0x1a), + ILI9881C_COMMAND_INSTR(0xaa, 0x27), + ILI9881C_COMMAND_INSTR(0xab, 0x49), + ILI9881C_COMMAND_INSTR(0xac, 0x1a), + ILI9881C_COMMAND_INSTR(0xad, 0x18), + ILI9881C_COMMAND_INSTR(0xae, 0x4c), + ILI9881C_COMMAND_INSTR(0xaf, 0x22), + ILI9881C_COMMAND_INSTR(0xb0, 0x27), + ILI9881C_COMMAND_INSTR(0xb1, 0x4b), + ILI9881C_COMMAND_INSTR(0xb2, 0x60), + ILI9881C_COMMAND_INSTR(0xb3, 0x39), + ILI9881C_COMMAND_INSTR(0xc0, 0x08), + ILI9881C_COMMAND_INSTR(0xc1, 0x11), + ILI9881C_COMMAND_INSTR(0xc2, 0x19), + ILI9881C_COMMAND_INSTR(0xc3, 0x0d), + ILI9881C_COMMAND_INSTR(0xc4, 0x0d), + ILI9881C_COMMAND_INSTR(0xc5, 0x1e), + ILI9881C_COMMAND_INSTR(0xc6, 0x14), + ILI9881C_COMMAND_INSTR(0xc7, 0x17), + ILI9881C_COMMAND_INSTR(0xc8, 0x4f), + ILI9881C_COMMAND_INSTR(0xc9, 0x1a), + ILI9881C_COMMAND_INSTR(0xca, 0x27), + ILI9881C_COMMAND_INSTR(0xcb, 0x49), + ILI9881C_COMMAND_INSTR(0xcc, 0x1a), + ILI9881C_COMMAND_INSTR(0xcd, 0x18), + ILI9881C_COMMAND_INSTR(0xce, 0x4c), + ILI9881C_COMMAND_INSTR(0xcf, 0x33), + ILI9881C_COMMAND_INSTR(0xd0, 0x27), + ILI9881C_COMMAND_INSTR(0xd1, 0x4b), + ILI9881C_COMMAND_INSTR(0xd2, 0x60), + ILI9881C_COMMAND_INSTR(0xd3, 0x39), + ILI9881C_SWITCH_PAGE_INSTR(0), + ILI9881C_COMMAND_INSTR(0x36, 0x03), +}; + static const struct ili9881c_instr w552946ab_init[] = { ILI9881C_SWITCH_PAGE_INSTR(3), ILI9881C_COMMAND_INSTR(0x01, 0x00), @@ -812,6 +980,23 @@ static const struct drm_display_mode k101_im2byl02_default_mode = { .height_mm = 217, }; +static const struct drm_display_mode tl050hdv35_default_mode = { + .clock = 59400, + + .hdisplay = 720, + .hsync_start = 720 + 18, + .hsync_end = 720 + 18 + 3, + .htotal = 720 + 18 + 3 + 20, + + .vdisplay = 1280, + .vsync_start = 1280 + 26, + .vsync_end = 1280 + 26 + 6, + .vtotal = 1280 + 26 + 6 + 28, + + .width_mm = 62, + .height_mm = 110, +}; + static const struct drm_display_mode w552946aba_default_mode = { .clock = 64000, @@ -944,6 +1129,14 @@ static const struct ili9881c_desc k101_im2byl02_desc = { .mode_flags = MIPI_DSI_MODE_VIDEO_SYNC_PULSE, }; +static const struct ili9881c_desc tl050hdv35_desc = { + .init = tl050hdv35_init, + .init_length = ARRAY_SIZE(tl050hdv35_init), + .mode = &tl050hdv35_default_mode, + .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | + MIPI_DSI_MODE_LPM, +}; + static const struct ili9881c_desc w552946aba_desc = { .init = w552946ab_init, .init_length = ARRAY_SIZE(w552946ab_init), @@ -955,6 +1148,7 @@ static const struct ili9881c_desc w552946aba_desc = { static const struct of_device_id ili9881c_of_match[] = { { .compatible = "bananapi,lhr050h41", .data = &lhr050h41_desc }, { .compatible = "feixin,k101-im2byl02", .data = &k101_im2byl02_desc }, + { .compatible = "tdo,tl050hdv35", .data = &tl050hdv35_desc }, { .compatible = "wanchanglong,w552946aba", .data = &w552946aba_desc }, { } }; diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index 4badda6570d5..72cef64441a6 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -1185,7 +1185,9 @@ static const struct panel_desc auo_t215hvn01 = { .delay = { .disable = 5, .unprepare = 1000, - } + }, + .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG, + .connector_type = DRM_MODE_CONNECTOR_LVDS, }; static const struct drm_display_mode avic_tm070ddh03_mode = { @@ -2374,6 +2376,37 @@ static const struct panel_desc innolux_g121x1_l03 = { }, }; +static const struct display_timing innolux_g156hce_l01_timings = { + .pixelclock = { 120000000, 144000000, 150000000 }, + .hactive = { 1920, 1920, 1920 }, + .hfront_porch = { 80, 90, 100 }, + .hback_porch = { 80, 90, 100 }, + .hsync_len = { 20, 30, 30 }, + .vactive = { 1080, 1080, 1080 }, + .vfront_porch = { 3, 10, 20 }, + .vback_porch = { 3, 10, 20 }, + .vsync_len = { 4, 10, 10 }, +}; + +static const struct panel_desc innolux_g156hce_l01 = { + .timings = &innolux_g156hce_l01_timings, + .num_timings = 1, + .bpc = 8, + .size = { + .width = 344, + .height = 194, + }, + .delay = { + .prepare = 1, /* T1+T2 */ + .enable = 450, /* T5 */ + .disable = 200, /* T6 */ + .unprepare = 10, /* T3+T7 */ + }, + .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG, + .bus_flags = DRM_BUS_FLAG_DE_HIGH, + .connector_type = DRM_MODE_CONNECTOR_LVDS, +}; + static const struct drm_display_mode innolux_n156bge_l21_mode = { .clock = 69300, .hdisplay = 1366, @@ -3205,6 +3238,7 @@ static const struct drm_display_mode powertip_ph800480t013_idf02_mode = { static const struct panel_desc powertip_ph800480t013_idf02 = { .modes = &powertip_ph800480t013_idf02_mode, .num_modes = 1, + .bpc = 8, .size = { .width = 152, .height = 91, @@ -4240,6 +4274,9 @@ static const struct of_device_id platform_of_match[] = { .compatible = "innolux,g121x1-l03", .data = &innolux_g121x1_l03, }, { + .compatible = "innolux,g156hce-l01", + .data = &innolux_g156hce_l01, + }, { .compatible = "innolux,n156bge-l21", .data = &innolux_n156bge_l21, }, { @@ -4455,13 +4492,13 @@ MODULE_DEVICE_TABLE(of, platform_of_match); static int panel_simple_platform_probe(struct platform_device *pdev) { - const struct of_device_id *id; + const struct panel_desc *desc; - id = of_match_node(platform_of_match, pdev->dev.of_node); - if (!id) + desc = of_device_get_match_data(&pdev->dev); + if (!desc) return -ENODEV; - return panel_simple_probe(&pdev->dev, id->data); + return panel_simple_probe(&pdev->dev, desc); } static void panel_simple_platform_remove(struct platform_device *pdev) @@ -4732,15 +4769,12 @@ MODULE_DEVICE_TABLE(of, dsi_of_match); static int panel_simple_dsi_probe(struct mipi_dsi_device *dsi) { const struct panel_desc_dsi *desc; - const struct of_device_id *id; int err; - id = of_match_node(dsi_of_match, dsi->dev.of_node); - if (!id) + desc = of_device_get_match_data(&dsi->dev); + if (!desc) return -ENODEV; - desc = id->data; - err = panel_simple_probe(&dsi->dev, &desc->desc); if (err < 0) return err; diff --git a/drivers/gpu/drm/panel/panel-sitronix-st7789v.c b/drivers/gpu/drm/panel/panel-sitronix-st7789v.c index bbc4569cbcdc..dc010d87a9ef 100644 --- a/drivers/gpu/drm/panel/panel-sitronix-st7789v.c +++ b/drivers/gpu/drm/panel/panel-sitronix-st7789v.c @@ -10,14 +10,12 @@ #include <linux/spi/spi.h> #include <video/mipi_display.h> +#include <linux/media-bus-format.h> #include <drm/drm_device.h> #include <drm/drm_modes.h> #include <drm/drm_panel.h> -#define ST7789V_COLMOD_RGB_FMT_18BITS (6 << 4) -#define ST7789V_COLMOD_CTRL_FMT_18BITS (6 << 0) - #define ST7789V_RAMCTRL_CMD 0xb0 #define ST7789V_RAMCTRL_RM_RGB BIT(4) #define ST7789V_RAMCTRL_DM_RGB BIT(0) @@ -29,7 +27,8 @@ #define ST7789V_RGBCTRL_RCM(n) (((n) & 3) << 5) #define ST7789V_RGBCTRL_VSYNC_HIGH BIT(3) #define ST7789V_RGBCTRL_HSYNC_HIGH BIT(2) -#define ST7789V_RGBCTRL_PCLK_HIGH BIT(1) +#define ST7789V_RGBCTRL_PCLK_FALLING BIT(1) +#define ST7789V_RGBCTRL_DE_LOW BIT(0) #define ST7789V_RGBCTRL_VBP(n) ((n) & 0x7f) #define ST7789V_RGBCTRL_HBP(n) ((n) & 0x1f) @@ -111,8 +110,19 @@ return val; \ } while (0) +#define ST7789V_IDS { 0x85, 0x85, 0x52 } +#define ST7789V_IDS_SIZE 3 + +struct st7789_panel_info { + const struct drm_display_mode *mode; + u32 bus_format; + u32 bus_flags; + bool invert_mode; +}; + struct st7789v { struct drm_panel panel; + const struct st7789_panel_info *info; struct spi_device *spi; struct gpio_desc *reset; struct regulator *power; @@ -132,17 +142,12 @@ static int st7789v_spi_write(struct st7789v *ctx, enum st7789v_prefix prefix, u8 data) { struct spi_transfer xfer = { }; - struct spi_message msg; u16 txbuf = ((prefix & 1) << 8) | data; - spi_message_init(&msg); - xfer.tx_buf = &txbuf; - xfer.bits_per_word = 9; xfer.len = sizeof(txbuf); - spi_message_add_tail(&xfer, &msg); - return spi_sync(ctx->spi, &msg); + return spi_sync_transfer(ctx->spi, &xfer, 1); } static int st7789v_write_command(struct st7789v *ctx, u8 cmd) @@ -155,6 +160,76 @@ static int st7789v_write_data(struct st7789v *ctx, u8 cmd) return st7789v_spi_write(ctx, ST7789V_DATA, cmd); } +static int st7789v_read_data(struct st7789v *ctx, u8 cmd, u8 *buf, + unsigned int len) +{ + struct spi_transfer xfer[2] = { }; + struct spi_message msg; + u16 txbuf = ((ST7789V_COMMAND & 1) << 8) | cmd; + u16 rxbuf[4] = {}; + u8 bit9 = 0; + int ret, i; + + switch (len) { + case 1: + case 3: + case 4: + break; + default: + return -EOPNOTSUPP; + } + + spi_message_init(&msg); + + xfer[0].tx_buf = &txbuf; + xfer[0].len = sizeof(txbuf); + spi_message_add_tail(&xfer[0], &msg); + + xfer[1].rx_buf = rxbuf; + xfer[1].len = len * 2; + spi_message_add_tail(&xfer[1], &msg); + + ret = spi_sync(ctx->spi, &msg); + if (ret) + return ret; + + for (i = 0; i < len; i++) { + buf[i] = rxbuf[i] >> i | (bit9 << (9 - i)); + if (i) + bit9 = rxbuf[i] & GENMASK(i - 1, 0); + } + + return 0; +} + +static int st7789v_check_id(struct drm_panel *panel) +{ + const u8 st7789v_ids[ST7789V_IDS_SIZE] = ST7789V_IDS; + struct st7789v *ctx = panel_to_st7789v(panel); + bool invalid_ids = false; + int ret, i; + u8 ids[3]; + + if (ctx->spi->mode & SPI_NO_RX) + return 0; + + ret = st7789v_read_data(ctx, MIPI_DCS_GET_DISPLAY_ID, ids, ST7789V_IDS_SIZE); + if (ret) + return ret; + + for (i = 0; i < ST7789V_IDS_SIZE; i++) { + if (ids[i] != st7789v_ids[i]) { + invalid_ids = true; + break; + } + } + + if (invalid_ids) + return -EIO; + + return 0; +} + static const struct drm_display_mode default_mode = { .clock = 7000, .hdisplay = 240, @@ -165,18 +240,76 @@ static const struct drm_display_mode default_mode = { .vsync_start = 320 + 8, .vsync_end = 320 + 8 + 4, .vtotal = 320 + 8 + 4 + 4, + .width_mm = 61, + .height_mm = 103, + .flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC, +}; + +static const struct drm_display_mode t28cp45tn89_mode = { + .clock = 6008, + .hdisplay = 240, + .hsync_start = 240 + 38, + .hsync_end = 240 + 38 + 10, + .htotal = 240 + 38 + 10 + 10, + .vdisplay = 320, + .vsync_start = 320 + 8, + .vsync_end = 320 + 8 + 4, + .vtotal = 320 + 8 + 4 + 4, + .width_mm = 43, + .height_mm = 57, + .flags = DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_NVSYNC, +}; + +static const struct drm_display_mode et028013dma_mode = { + .clock = 3000, + .hdisplay = 240, + .hsync_start = 240 + 38, + .hsync_end = 240 + 38 + 10, + .htotal = 240 + 38 + 10 + 10, + .vdisplay = 320, + .vsync_start = 320 + 8, + .vsync_end = 320 + 8 + 4, + .vtotal = 320 + 8 + 4 + 4, + .width_mm = 43, + .height_mm = 58, + .flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC, +}; + +static const struct st7789_panel_info default_panel = { + .mode = &default_mode, + .invert_mode = true, + .bus_format = MEDIA_BUS_FMT_RGB666_1X18, + .bus_flags = DRM_BUS_FLAG_DE_HIGH | + DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE, +}; + +static const struct st7789_panel_info t28cp45tn89_panel = { + .mode = &t28cp45tn89_mode, + .invert_mode = false, + .bus_format = MEDIA_BUS_FMT_RGB565_1X16, + .bus_flags = DRM_BUS_FLAG_DE_HIGH | + DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE, +}; + +static const struct st7789_panel_info et028013dma_panel = { + .mode = &et028013dma_mode, + .invert_mode = true, + .bus_format = MEDIA_BUS_FMT_RGB666_1X18, + .bus_flags = DRM_BUS_FLAG_DE_HIGH | + DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE, }; static int st7789v_get_modes(struct drm_panel *panel, struct drm_connector *connector) { + struct st7789v *ctx = panel_to_st7789v(panel); struct drm_display_mode *mode; - mode = drm_mode_duplicate(connector->dev, &default_mode); + mode = drm_mode_duplicate(connector->dev, ctx->info->mode); if (!mode) { - dev_err(panel->dev, "failed to add mode %ux%ux@%u\n", - default_mode.hdisplay, default_mode.vdisplay, - drm_mode_vrefresh(&default_mode)); + dev_err(panel->dev, "failed to add mode %ux%u@%u\n", + ctx->info->mode->hdisplay, ctx->info->mode->vdisplay, + drm_mode_vrefresh(ctx->info->mode)); return -ENOMEM; } @@ -185,8 +318,12 @@ 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); - connector->display_info.width_mm = 61; - connector->display_info.height_mm = 103; + connector->display_info.bpc = 6; + connector->display_info.width_mm = ctx->info->mode->width_mm; + connector->display_info.height_mm = ctx->info->mode->height_mm; + connector->display_info.bus_flags = ctx->info->bus_flags; + drm_display_info_set_bus_formats(&connector->display_info, + &ctx->info->bus_format, 1); return 1; } @@ -194,8 +331,34 @@ static int st7789v_get_modes(struct drm_panel *panel, static int st7789v_prepare(struct drm_panel *panel) { struct st7789v *ctx = panel_to_st7789v(panel); + u8 pixel_fmt, polarity; int ret; + switch (ctx->info->bus_format) { + case MEDIA_BUS_FMT_RGB666_1X18: + pixel_fmt = MIPI_DCS_PIXEL_FMT_18BIT; + break; + case MEDIA_BUS_FMT_RGB565_1X16: + pixel_fmt = MIPI_DCS_PIXEL_FMT_16BIT; + break; + default: + dev_err(panel->dev, "unsupported bus format: %d\n", + ctx->info->bus_format); + return -EINVAL; + } + + pixel_fmt = (pixel_fmt << 4) | pixel_fmt; + + polarity = 0; + if (ctx->info->mode->flags & DRM_MODE_FLAG_PVSYNC) + polarity |= ST7789V_RGBCTRL_VSYNC_HIGH; + if (ctx->info->mode->flags & DRM_MODE_FLAG_PHSYNC) + polarity |= ST7789V_RGBCTRL_HSYNC_HIGH; + if (ctx->info->bus_flags & DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE) + polarity |= ST7789V_RGBCTRL_PCLK_FALLING; + if (ctx->info->bus_flags & DRM_BUS_FLAG_DE_LOW) + polarity |= ST7789V_RGBCTRL_DE_LOW; + ret = regulator_enable(ctx->power); if (ret) return ret; @@ -205,6 +368,14 @@ static int st7789v_prepare(struct drm_panel *panel) gpiod_set_value(ctx->reset, 0); msleep(120); + /* + * Avoid failing if the IDs are invalid in case the Rx bus width + * description is missing. + */ + ret = st7789v_check_id(panel); + if (ret) + dev_warn(panel->dev, "Unrecognized panel IDs"); + ST7789V_TEST(ret, st7789v_write_command(ctx, MIPI_DCS_EXIT_SLEEP_MODE)); /* We need to wait 120ms after a sleep out command */ @@ -216,9 +387,7 @@ static int st7789v_prepare(struct drm_panel *panel) ST7789V_TEST(ret, st7789v_write_command(ctx, MIPI_DCS_SET_PIXEL_FORMAT)); - ST7789V_TEST(ret, st7789v_write_data(ctx, - (MIPI_DCS_PIXEL_FMT_18BIT << 4) | - (MIPI_DCS_PIXEL_FMT_18BIT))); + ST7789V_TEST(ret, st7789v_write_data(ctx, pixel_fmt)); ST7789V_TEST(ret, st7789v_write_command(ctx, ST7789V_PORCTRL_CMD)); ST7789V_TEST(ret, st7789v_write_data(ctx, 0xc)); @@ -296,7 +465,13 @@ static int st7789v_prepare(struct drm_panel *panel) ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_NVGAMCTRL_VN61(0x1b))); ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_NVGAMCTRL_VN62(0x28))); - ST7789V_TEST(ret, st7789v_write_command(ctx, MIPI_DCS_ENTER_INVERT_MODE)); + if (ctx->info->invert_mode) { + ST7789V_TEST(ret, st7789v_write_command(ctx, + MIPI_DCS_ENTER_INVERT_MODE)); + } else { + ST7789V_TEST(ret, st7789v_write_command(ctx, + MIPI_DCS_EXIT_INVERT_MODE)); + } ST7789V_TEST(ret, st7789v_write_command(ctx, ST7789V_RAMCTRL_CMD)); ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_RAMCTRL_DM_RGB | @@ -307,9 +482,7 @@ static int st7789v_prepare(struct drm_panel *panel) ST7789V_TEST(ret, st7789v_write_command(ctx, ST7789V_RGBCTRL_CMD)); ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_RGBCTRL_WO | ST7789V_RGBCTRL_RCM(2) | - ST7789V_RGBCTRL_VSYNC_HIGH | - ST7789V_RGBCTRL_HSYNC_HIGH | - ST7789V_RGBCTRL_PCLK_HIGH)); + polarity)); ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_RGBCTRL_VBP(8))); ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_RGBCTRL_HBP(20))); @@ -355,32 +528,40 @@ static const struct drm_panel_funcs st7789v_drm_funcs = { static int st7789v_probe(struct spi_device *spi) { + struct device *dev = &spi->dev; struct st7789v *ctx; int ret; - ctx = devm_kzalloc(&spi->dev, sizeof(*ctx), GFP_KERNEL); + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) return -ENOMEM; spi_set_drvdata(spi, ctx); ctx->spi = spi; - drm_panel_init(&ctx->panel, &spi->dev, &st7789v_drm_funcs, + spi->bits_per_word = 9; + ret = spi_setup(spi); + if (ret < 0) + return dev_err_probe(&spi->dev, ret, "Failed to setup spi\n"); + + ctx->info = device_get_match_data(&spi->dev); + + drm_panel_init(&ctx->panel, dev, &st7789v_drm_funcs, DRM_MODE_CONNECTOR_DPI); - ctx->power = devm_regulator_get(&spi->dev, "power"); - if (IS_ERR(ctx->power)) - return PTR_ERR(ctx->power); + ctx->power = devm_regulator_get(dev, "power"); + ret = PTR_ERR_OR_ZERO(ctx->power); + if (ret) + return dev_err_probe(dev, ret, "Failed to get regulator\n"); - ctx->reset = devm_gpiod_get(&spi->dev, "reset", GPIOD_OUT_LOW); - if (IS_ERR(ctx->reset)) { - dev_err(&spi->dev, "Couldn't get our reset line\n"); - return PTR_ERR(ctx->reset); - } + ctx->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + ret = PTR_ERR_OR_ZERO(ctx->reset); + if (ret) + return dev_err_probe(dev, ret, "Failed to get reset line\n"); ret = drm_panel_of_backlight(&ctx->panel); if (ret) - return ret; + return dev_err_probe(dev, ret, "Failed to get backlight\n"); drm_panel_add(&ctx->panel); @@ -394,8 +575,18 @@ static void st7789v_remove(struct spi_device *spi) drm_panel_remove(&ctx->panel); } +static const struct spi_device_id st7789v_spi_id[] = { + { "st7789v", (unsigned long) &default_panel }, + { "t28cp45tn89-v17", (unsigned long) &t28cp45tn89_panel }, + { "et028013dma", (unsigned long) &et028013dma_panel }, + { } +}; +MODULE_DEVICE_TABLE(spi, st7789v_spi_id); + static const struct of_device_id st7789v_of_match[] = { - { .compatible = "sitronix,st7789v" }, + { .compatible = "sitronix,st7789v", .data = &default_panel }, + { .compatible = "inanbo,t28cp45tn89-v17", .data = &t28cp45tn89_panel }, + { .compatible = "edt,et028013dma", .data = &et028013dma_panel }, { } }; MODULE_DEVICE_TABLE(of, st7789v_of_match); @@ -403,6 +594,7 @@ MODULE_DEVICE_TABLE(of, st7789v_of_match); static struct spi_driver st7789v_driver = { .probe = st7789v_probe, .remove = st7789v_remove, + .id_table = st7789v_spi_id, .driver = { .name = "st7789v", .of_match_table = st7789v_of_match, diff --git a/drivers/gpu/drm/panel/panel-startek-kd070fhfid015.c b/drivers/gpu/drm/panel/panel-startek-kd070fhfid015.c new file mode 100644 index 000000000000..6e77a2d71d81 --- /dev/null +++ b/drivers/gpu/drm/panel/panel-startek-kd070fhfid015.c @@ -0,0 +1,406 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2016 InforceComputing + * Copyright (C) 2016 Linaro Ltd + * Copyright (C) 2023 BayLibre, SAS + * + * Authors: + * - Vinay Simha BN <simhavcs@gmail.com> + * - Sumit Semwal <sumit.semwal@linaro.org> + * - Guillaume La Roque <glaroque@baylibre.com> + * + */ + +#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> + +#define DSI_REG_MCAP 0xB0 +#define DSI_REG_IS 0xB3 /* Interface Setting */ +#define DSI_REG_IIS 0xB4 /* Interface ID Setting */ +#define DSI_REG_CTRL 0xB6 + +enum { + IOVCC = 0, + POWER = 1 +}; + +struct stk_panel { + bool prepared; + const struct drm_display_mode *mode; + struct backlight_device *backlight; + struct drm_panel base; + struct gpio_desc *enable_gpio; /* Power IC supply enable */ + struct gpio_desc *reset_gpio; /* External reset */ + struct mipi_dsi_device *dsi; + struct regulator_bulk_data supplies[2]; +}; + +static inline struct stk_panel *to_stk_panel(struct drm_panel *panel) +{ + return container_of(panel, struct stk_panel, base); +} + +static int stk_panel_init(struct stk_panel *stk) +{ + struct mipi_dsi_device *dsi = stk->dsi; + struct device *dev = &stk->dsi->dev; + int ret; + + ret = mipi_dsi_dcs_soft_reset(dsi); + if (ret < 0) { + dev_err(dev, "failed to mipi_dsi_dcs_soft_reset: %d\n", ret); + return ret; + } + mdelay(5); + + ret = mipi_dsi_dcs_exit_sleep_mode(dsi); + if (ret < 0) { + dev_err(dev, "failed to set exit sleep mode: %d\n", ret); + return ret; + } + msleep(120); + + mipi_dsi_generic_write_seq(dsi, DSI_REG_MCAP, 0x04); + + /* Interface setting, video mode */ + mipi_dsi_generic_write_seq(dsi, DSI_REG_IS, 0x14, 0x08, 0x00, 0x22, 0x00); + mipi_dsi_generic_write_seq(dsi, DSI_REG_IIS, 0x0C, 0x00); + mipi_dsi_generic_write_seq(dsi, DSI_REG_CTRL, 0x3A, 0xD3); + + ret = mipi_dsi_dcs_set_display_brightness(dsi, 0x77); + if (ret < 0) { + dev_err(dev, "failed to write display brightness: %d\n", ret); + return ret; + } + + mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY, + MIPI_DCS_WRITE_MEMORY_START); + + ret = mipi_dsi_dcs_set_pixel_format(dsi, 0x77); + if (ret < 0) { + dev_err(dev, "failed to set pixel format: %d\n", ret); + return ret; + } + + ret = mipi_dsi_dcs_set_column_address(dsi, 0, stk->mode->hdisplay - 1); + if (ret < 0) { + dev_err(dev, "failed to set column address: %d\n", ret); + return ret; + } + + ret = mipi_dsi_dcs_set_page_address(dsi, 0, stk->mode->vdisplay - 1); + if (ret < 0) { + dev_err(dev, "failed to set page address: %d\n", ret); + return ret; + } + + return 0; +} + +static int stk_panel_on(struct stk_panel *stk) +{ + struct mipi_dsi_device *dsi = stk->dsi; + struct device *dev = &stk->dsi->dev; + int ret; + + ret = mipi_dsi_dcs_set_display_on(dsi); + if (ret < 0) + dev_err(dev, "failed to set display on: %d\n", ret); + + mdelay(20); + + return ret; +} + +static void stk_panel_off(struct stk_panel *stk) +{ + struct mipi_dsi_device *dsi = stk->dsi; + struct device *dev = &stk->dsi->dev; + int ret; + + dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; + + ret = mipi_dsi_dcs_set_display_off(dsi); + if (ret < 0) + dev_err(dev, "failed to set display off: %d\n", ret); + + ret = mipi_dsi_dcs_enter_sleep_mode(dsi); + if (ret < 0) + dev_err(dev, "failed to enter sleep mode: %d\n", ret); + + msleep(100); +} + +static int stk_panel_unprepare(struct drm_panel *panel) +{ + struct stk_panel *stk = to_stk_panel(panel); + + if (!stk->prepared) + return 0; + + stk_panel_off(stk); + regulator_bulk_disable(ARRAY_SIZE(stk->supplies), stk->supplies); + gpiod_set_value(stk->reset_gpio, 0); + gpiod_set_value(stk->enable_gpio, 1); + + stk->prepared = false; + + return 0; +} + +static int stk_panel_prepare(struct drm_panel *panel) +{ + struct stk_panel *stk = to_stk_panel(panel); + struct device *dev = &stk->dsi->dev; + int ret; + + if (stk->prepared) + return 0; + + gpiod_set_value(stk->reset_gpio, 0); + gpiod_set_value(stk->enable_gpio, 0); + ret = regulator_enable(stk->supplies[IOVCC].consumer); + if (ret < 0) + return ret; + + mdelay(8); + ret = regulator_enable(stk->supplies[POWER].consumer); + if (ret < 0) + goto iovccoff; + + mdelay(20); + gpiod_set_value(stk->enable_gpio, 1); + mdelay(20); + gpiod_set_value(stk->reset_gpio, 1); + mdelay(10); + ret = stk_panel_init(stk); + if (ret < 0) { + dev_err(dev, "failed to init panel: %d\n", ret); + goto poweroff; + } + + ret = stk_panel_on(stk); + if (ret < 0) { + dev_err(dev, "failed to set panel on: %d\n", ret); + goto poweroff; + } + + stk->prepared = true; + + return 0; + +poweroff: + regulator_disable(stk->supplies[POWER].consumer); +iovccoff: + regulator_disable(stk->supplies[IOVCC].consumer); + gpiod_set_value(stk->reset_gpio, 0); + gpiod_set_value(stk->enable_gpio, 0); + + return ret; +} + +static const struct drm_display_mode default_mode = { + .clock = 163204, + .hdisplay = 1200, + .hsync_start = 1200 + 144, + .hsync_end = 1200 + 144 + 16, + .htotal = 1200 + 144 + 16 + 45, + .vdisplay = 1920, + .vsync_start = 1920 + 8, + .vsync_end = 1920 + 8 + 4, + .vtotal = 1920 + 8 + 4 + 4, + .width_mm = 95, + .height_mm = 151, +}; + +static int stk_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) { + dev_err(panel->dev, "failed to add mode %ux%ux@%u\n", + default_mode.hdisplay, default_mode.vdisplay, + drm_mode_vrefresh(&default_mode)); + return -ENOMEM; + } + + drm_mode_set_name(mode); + drm_mode_probed_add(connector, mode); + connector->display_info.width_mm = default_mode.width_mm; + connector->display_info.height_mm = default_mode.height_mm; + return 1; +} + +static int dsi_dcs_bl_get_brightness(struct backlight_device *bl) +{ + struct mipi_dsi_device *dsi = bl_get_data(bl); + int ret; + u16 brightness; + + dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; + ret = mipi_dsi_dcs_get_display_brightness(dsi, &brightness); + if (ret < 0) + return ret; + + dsi->mode_flags |= MIPI_DSI_MODE_LPM; + return brightness & 0xff; +} + +static int dsi_dcs_bl_update_status(struct backlight_device *bl) +{ + struct mipi_dsi_device *dsi = bl_get_data(bl); + struct device *dev = &dsi->dev; + int ret; + + dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; + ret = mipi_dsi_dcs_set_display_brightness(dsi, bl->props.brightness); + if (ret < 0) { + dev_err(dev, "failed to set DSI control: %d\n", ret); + return ret; + } + + dsi->mode_flags |= MIPI_DSI_MODE_LPM; + return 0; +} + +static const struct backlight_ops dsi_bl_ops = { + .update_status = dsi_dcs_bl_update_status, + .get_brightness = dsi_dcs_bl_get_brightness, +}; + +static struct backlight_device * +drm_panel_create_dsi_backlight(struct mipi_dsi_device *dsi) +{ + struct device *dev = &dsi->dev; + struct backlight_properties props = { + .type = BACKLIGHT_RAW, + .brightness = 255, + .max_brightness = 255, + }; + + return devm_backlight_device_register(dev, dev_name(dev), dev, dsi, + &dsi_bl_ops, &props); +} + +static const struct drm_panel_funcs stk_panel_funcs = { + .unprepare = stk_panel_unprepare, + .prepare = stk_panel_prepare, + .get_modes = stk_panel_get_modes, +}; + +static const struct of_device_id stk_of_match[] = { + { .compatible = "startek,kd070fhfid015", }, + { } +}; +MODULE_DEVICE_TABLE(of, stk_of_match); + +static int stk_panel_add(struct stk_panel *stk) +{ + struct device *dev = &stk->dsi->dev; + int ret; + + stk->mode = &default_mode; + + stk->supplies[IOVCC].supply = "iovcc"; + stk->supplies[POWER].supply = "power"; + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(stk->supplies), stk->supplies); + if (ret) { + dev_err(dev, "regulator_bulk failed\n"); + return ret; + } + + stk->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(stk->reset_gpio)) { + ret = PTR_ERR(stk->reset_gpio); + dev_err(dev, "cannot get reset-gpios %d\n", ret); + return ret; + } + + stk->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW); + if (IS_ERR(stk->enable_gpio)) { + ret = PTR_ERR(stk->enable_gpio); + dev_err(dev, "cannot get enable-gpio %d\n", ret); + return ret; + } + + stk->backlight = drm_panel_create_dsi_backlight(stk->dsi); + if (IS_ERR(stk->backlight)) { + ret = PTR_ERR(stk->backlight); + dev_err(dev, "failed to register backlight %d\n", ret); + return ret; + } + + drm_panel_init(&stk->base, &stk->dsi->dev, &stk_panel_funcs, + DRM_MODE_CONNECTOR_DSI); + + drm_panel_add(&stk->base); + + return 0; +} + +static int stk_panel_probe(struct mipi_dsi_device *dsi) +{ + struct stk_panel *stk; + int ret; + + dsi->lanes = 4; + dsi->format = MIPI_DSI_FMT_RGB888; + dsi->mode_flags = (MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM); + + stk = devm_kzalloc(&dsi->dev, sizeof(*stk), GFP_KERNEL); + if (!stk) + return -ENOMEM; + + mipi_dsi_set_drvdata(dsi, stk); + + stk->dsi = dsi; + + ret = stk_panel_add(stk); + if (ret < 0) + return ret; + + ret = mipi_dsi_attach(dsi); + if (ret < 0) + drm_panel_remove(&stk->base); + + return 0; +} + +static void stk_panel_remove(struct mipi_dsi_device *dsi) +{ + struct stk_panel *stk = mipi_dsi_get_drvdata(dsi); + int err; + + err = mipi_dsi_detach(dsi); + if (err < 0) + dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", + err); + + drm_panel_remove(&stk->base); +} + +static struct mipi_dsi_driver stk_panel_driver = { + .driver = { + .name = "panel-startek-kd070fhfid015", + .of_match_table = stk_of_match, + }, + .probe = stk_panel_probe, + .remove = stk_panel_remove, +}; +module_mipi_dsi_driver(stk_panel_driver); + +MODULE_AUTHOR("Guillaume La Roque <glaroque@baylibre.com>"); +MODULE_DESCRIPTION("STARTEK KD070FHFID015"); +MODULE_LICENSE("GPL"); |