Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

gpu/drm: panel: Add Sharp LQ079L1SX01 panel support

This panel requires dual-channel mode. The device accepts video-mode data
on 8 lanes and will therefore need a dual-channel DSI controller. The two
interfaces that make up this device need to be instantiated in the
controllers that gang up to provide the dual-channel DSI host.

Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
Link: https://lore.kernel.org/r/20250919153839.236241-3-clamor95@gmail.com

authored by

Svyatoslav Ryhel and committed by
Neil Armstrong
306e6407 16c5b1a6

+241
+15
drivers/gpu/drm/panel/Kconfig
··· 888 888 Say Y here if you want to enable support for the Seiko 889 889 43WVF1G controller for 800x480 LCD panels 890 890 891 + config DRM_PANEL_SHARP_LQ079L1SX01 892 + tristate "Sharp LQ079L1SX01 panel" 893 + depends on OF 894 + depends on DRM_MIPI_DSI 895 + depends on BACKLIGHT_CLASS_DEVICE 896 + select VIDEOMODE_HELPERS 897 + help 898 + Say Y here if you want to enable support for Sharp LQ079L1SX01 899 + TFT-LCD modules. The panel has a 2560x1600 resolution and uses 900 + 24 bit RGB per pixel. It provides a dual MIPI DSI interface to 901 + the host. 902 + 903 + To compile this driver as a module, choose M here: the module 904 + will be called panel-sharp-lq079l1sx01. 905 + 891 906 config DRM_PANEL_SHARP_LQ101R1SX01 892 907 tristate "Sharp LQ101R1SX01 panel" 893 908 depends on OF
+1
drivers/gpu/drm/panel/Makefile
··· 91 91 obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA5X01_AMS561RA01) += panel-samsung-s6e8aa5x01-ams561ra01.o 92 92 obj-$(CONFIG_DRM_PANEL_SAMSUNG_SOFEF00) += panel-samsung-sofef00.o 93 93 obj-$(CONFIG_DRM_PANEL_SEIKO_43WVF1G) += panel-seiko-43wvf1g.o 94 + obj-$(CONFIG_DRM_PANEL_SHARP_LQ079L1SX01) += panel-sharp-lq079l1sx01.o 94 95 obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o 95 96 obj-$(CONFIG_DRM_PANEL_SHARP_LS037V7DW01) += panel-sharp-ls037v7dw01.o 96 97 obj-$(CONFIG_DRM_PANEL_SHARP_LS043T1LE01) += panel-sharp-ls043t1le01.o
+225
drivers/gpu/drm/panel/panel-sharp-lq079l1sx01.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Copyright (c) 2016 XiaoMi, Inc. 4 + * Copyright (c) 2024 Svyatoslav Ryhel <clamor95@gmail.com> 5 + */ 6 + 7 + #include <linux/delay.h> 8 + #include <linux/gpio/consumer.h> 9 + #include <linux/module.h> 10 + #include <linux/of.h> 11 + #include <linux/of_graph.h> 12 + #include <linux/regulator/consumer.h> 13 + 14 + #include <video/mipi_display.h> 15 + 16 + #include <drm/drm_connector.h> 17 + #include <drm/drm_crtc.h> 18 + #include <drm/drm_device.h> 19 + #include <drm/drm_mipi_dsi.h> 20 + #include <drm/drm_modes.h> 21 + #include <drm/drm_panel.h> 22 + #include <drm/drm_probe_helper.h> 23 + 24 + static const struct regulator_bulk_data sharp_supplies[] = { 25 + { .supply = "avdd" }, { .supply = "vddio" }, 26 + { .supply = "vsp" }, { .supply = "vsn" }, 27 + }; 28 + 29 + struct sharp_panel { 30 + struct drm_panel panel; 31 + struct mipi_dsi_device *dsi[2]; 32 + 33 + struct gpio_desc *reset_gpio; 34 + struct regulator_bulk_data *supplies; 35 + 36 + const struct drm_display_mode *mode; 37 + }; 38 + 39 + static inline struct sharp_panel *to_sharp_panel(struct drm_panel *panel) 40 + { 41 + return container_of(panel, struct sharp_panel, panel); 42 + } 43 + 44 + static void sharp_panel_reset(struct sharp_panel *sharp) 45 + { 46 + gpiod_set_value_cansleep(sharp->reset_gpio, 1); 47 + usleep_range(2000, 3000); 48 + gpiod_set_value_cansleep(sharp->reset_gpio, 0); 49 + usleep_range(2000, 3000); 50 + } 51 + 52 + static int sharp_panel_prepare(struct drm_panel *panel) 53 + { 54 + struct sharp_panel *sharp = to_sharp_panel(panel); 55 + struct device *dev = panel->dev; 56 + struct mipi_dsi_device *dsi0 = sharp->dsi[0]; 57 + struct mipi_dsi_device *dsi1 = sharp->dsi[1]; 58 + struct mipi_dsi_multi_context dsi_ctx = { .dsi = NULL }; 59 + int ret; 60 + 61 + ret = regulator_bulk_enable(ARRAY_SIZE(sharp_supplies), sharp->supplies); 62 + if (ret) { 63 + dev_err(dev, "error enabling regulators (%d)\n", ret); 64 + return ret; 65 + } 66 + 67 + msleep(24); 68 + 69 + if (sharp->reset_gpio) 70 + sharp_panel_reset(sharp); 71 + 72 + msleep(32); 73 + 74 + mipi_dsi_dual_dcs_write_seq_multi(&dsi_ctx, dsi0, dsi1, MIPI_DCS_EXIT_SLEEP_MODE); 75 + mipi_dsi_msleep(&dsi_ctx, 120); 76 + 77 + mipi_dsi_dual_dcs_write_seq_multi(&dsi_ctx, dsi0, dsi1, 78 + MIPI_DCS_SET_DISPLAY_BRIGHTNESS, 0xff); 79 + mipi_dsi_dual_dcs_write_seq_multi(&dsi_ctx, dsi0, dsi1, 80 + MIPI_DCS_WRITE_POWER_SAVE, 0x01); 81 + mipi_dsi_dual_dcs_write_seq_multi(&dsi_ctx, dsi0, dsi1, 82 + MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x2c); 83 + 84 + mipi_dsi_dual_dcs_write_seq_multi(&dsi_ctx, dsi0, dsi1, MIPI_DCS_SET_DISPLAY_ON); 85 + 86 + return 0; 87 + } 88 + 89 + static int sharp_panel_unprepare(struct drm_panel *panel) 90 + { 91 + struct sharp_panel *sharp = to_sharp_panel(panel); 92 + struct mipi_dsi_device *dsi0 = sharp->dsi[0]; 93 + struct mipi_dsi_device *dsi1 = sharp->dsi[1]; 94 + struct mipi_dsi_multi_context dsi_ctx = { .dsi = NULL }; 95 + 96 + mipi_dsi_dual_dcs_write_seq_multi(&dsi_ctx, dsi0, dsi1, MIPI_DCS_SET_DISPLAY_OFF); 97 + mipi_dsi_msleep(&dsi_ctx, 100); 98 + mipi_dsi_dual_dcs_write_seq_multi(&dsi_ctx, dsi0, dsi1, MIPI_DCS_ENTER_SLEEP_MODE); 99 + mipi_dsi_msleep(&dsi_ctx, 150); 100 + 101 + if (sharp->reset_gpio) 102 + gpiod_set_value_cansleep(sharp->reset_gpio, 1); 103 + 104 + return regulator_bulk_disable(ARRAY_SIZE(sharp_supplies), sharp->supplies); 105 + } 106 + 107 + static const struct drm_display_mode default_mode = { 108 + .clock = (1536 + 136 + 28 + 28) * (2048 + 14 + 8 + 2) * 60 / 1000, 109 + .hdisplay = 1536, 110 + .hsync_start = 1536 + 136, 111 + .hsync_end = 1536 + 136 + 28, 112 + .htotal = 1536 + 136 + 28 + 28, 113 + .vdisplay = 2048, 114 + .vsync_start = 2048 + 14, 115 + .vsync_end = 2048 + 14 + 8, 116 + .vtotal = 2048 + 14 + 8 + 2, 117 + .width_mm = 120, 118 + .height_mm = 160, 119 + .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED, 120 + }; 121 + 122 + static int sharp_panel_get_modes(struct drm_panel *panel, 123 + struct drm_connector *connector) 124 + { 125 + return drm_connector_helper_get_modes_fixed(connector, &default_mode); 126 + } 127 + 128 + static const struct drm_panel_funcs sharp_panel_funcs = { 129 + .unprepare = sharp_panel_unprepare, 130 + .prepare = sharp_panel_prepare, 131 + .get_modes = sharp_panel_get_modes, 132 + }; 133 + 134 + static int sharp_panel_probe(struct mipi_dsi_device *dsi) 135 + { 136 + const struct mipi_dsi_device_info info = { "sharp-link1", 0, NULL }; 137 + struct device *dev = &dsi->dev; 138 + struct device_node *dsi_r; 139 + struct mipi_dsi_host *dsi_r_host; 140 + struct sharp_panel *sharp; 141 + int i, ret; 142 + 143 + sharp = devm_drm_panel_alloc(dev, struct sharp_panel, panel, 144 + &sharp_panel_funcs, DRM_MODE_CONNECTOR_DSI); 145 + if (IS_ERR(sharp)) 146 + return PTR_ERR(sharp); 147 + 148 + ret = devm_regulator_bulk_get_const(dev, ARRAY_SIZE(sharp_supplies), 149 + sharp_supplies, &sharp->supplies); 150 + if (ret) 151 + return dev_err_probe(dev, ret, "failed to get supplies\n"); 152 + 153 + sharp->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); 154 + if (IS_ERR(sharp->reset_gpio)) 155 + return dev_err_probe(dev, PTR_ERR(sharp->reset_gpio), 156 + "failed to get reset GPIO\n"); 157 + 158 + /* Panel is always connected to two DSI hosts, DSI0 is left, DSI1 is right */ 159 + dsi_r = of_graph_get_remote_node(dsi->dev.of_node, 1, -1); 160 + if (!dsi_r) 161 + return dev_err_probe(dev, -ENODEV, "failed to find second DSI host node\n"); 162 + 163 + dsi_r_host = of_find_mipi_dsi_host_by_node(dsi_r); 164 + of_node_put(dsi_r); 165 + if (!dsi_r_host) 166 + return dev_err_probe(dev, -EPROBE_DEFER, "cannot get secondary DSI host\n"); 167 + 168 + sharp->dsi[1] = devm_mipi_dsi_device_register_full(dev, dsi_r_host, &info); 169 + if (IS_ERR(sharp->dsi[1])) 170 + return dev_err_probe(dev, PTR_ERR(sharp->dsi[1]), 171 + "second link registration failed\n"); 172 + 173 + sharp->dsi[0] = dsi; 174 + mipi_dsi_set_drvdata(dsi, sharp); 175 + 176 + ret = drm_panel_of_backlight(&sharp->panel); 177 + if (ret) 178 + return dev_err_probe(dev, ret, "Failed to get backlight\n"); 179 + 180 + drm_panel_add(&sharp->panel); 181 + 182 + for (i = 0; i < ARRAY_SIZE(sharp->dsi); i++) { 183 + if (!sharp->dsi[i]) 184 + continue; 185 + 186 + sharp->dsi[i]->lanes = 4; 187 + sharp->dsi[i]->format = MIPI_DSI_FMT_RGB888; 188 + sharp->dsi[i]->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM; 189 + 190 + ret = devm_mipi_dsi_attach(dev, sharp->dsi[i]); 191 + if (ret < 0) { 192 + drm_panel_remove(&sharp->panel); 193 + return dev_err_probe(dev, ret, "failed to attach to DSI%d\n", i); 194 + } 195 + } 196 + 197 + return 0; 198 + } 199 + 200 + static void sharp_panel_remove(struct mipi_dsi_device *dsi) 201 + { 202 + struct sharp_panel *sharp = mipi_dsi_get_drvdata(dsi); 203 + 204 + drm_panel_remove(&sharp->panel); 205 + } 206 + 207 + static const struct of_device_id sharp_of_match[] = { 208 + { .compatible = "sharp,lq079l1sx01" }, 209 + { } 210 + }; 211 + MODULE_DEVICE_TABLE(of, sharp_of_match); 212 + 213 + static struct mipi_dsi_driver sharp_panel_driver = { 214 + .driver = { 215 + .name = "panel-sharp-lq079l1sx01", 216 + .of_match_table = sharp_of_match, 217 + }, 218 + .probe = sharp_panel_probe, 219 + .remove = sharp_panel_remove, 220 + }; 221 + module_mipi_dsi_driver(sharp_panel_driver); 222 + 223 + MODULE_AUTHOR("Svyatoslav Ryhel <clamor95@gmail.com>"); 224 + MODULE_DESCRIPTION("Sharp LQ079L1SX01 panel driver"); 225 + MODULE_LICENSE("GPL");