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

drm: panel: Add support for Renesas R69328 based MIPI DSI panel

Driver adds support for panels with Renesas R69328 IC

Currently supported compatible is:
- jdi,dx12d100vm0eaa

Co-developed-by: Svyatoslav Ryhel <clamor95@gmail.com>
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
Signed-off-by: Maxim Schwalm <maxim.schwalm@gmail.com>
Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Link: https://lore.kernel.org/r/20250506092718.106088-5-clamor95@gmail.com
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>

authored by

Maxim Schwalm and committed by
Dmitry Baryshkov
9e0f93f7 215c73d4

+295
+13
drivers/gpu/drm/panel/Kconfig
··· 658 658 This panel controller can be found in LG Optimus Vu P895 smartphone 659 659 in combination with LCD panel. 660 660 661 + config DRM_PANEL_RENESAS_R69328 662 + tristate "Renesas R69328 720x1280 DSI video mode panel" 663 + depends on OF 664 + depends on DRM_MIPI_DSI 665 + depends on BACKLIGHT_CLASS_DEVICE 666 + help 667 + Say Y here if you want to enable support for JDI dx12d100vm0eaa 668 + IPS-LCD module with Renesas R69328 IC. The panel has a 720x1280 669 + resolution and uses 24 bit RGB per pixel. 670 + 671 + This panel controller can be found in LG Optimus 4X P895 smartphone 672 + in combination with LCD panel. 673 + 661 674 config DRM_PANEL_RONBO_RB070D30 662 675 tristate "Ronbo Electronics RB070D30 panel" 663 676 depends on OF
+1
drivers/gpu/drm/panel/Makefile
··· 66 66 obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM692E5) += panel-raydium-rm692e5.o 67 67 obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM69380) += panel-raydium-rm69380.o 68 68 obj-$(CONFIG_DRM_PANEL_RENESAS_R61307) += panel-renesas-r61307.o 69 + obj-$(CONFIG_DRM_PANEL_RENESAS_R69328) += panel-renesas-r69328.o 69 70 obj-$(CONFIG_DRM_PANEL_RONBO_RB070D30) += panel-ronbo-rb070d30.o 70 71 obj-$(CONFIG_DRM_PANEL_SAMSUNG_AMS581VF01) += panel-samsung-ams581vf01.o 71 72 obj-$(CONFIG_DRM_PANEL_SAMSUNG_AMS639RQ08) += panel-samsung-ams639rq08.o
+281
drivers/gpu/drm/panel/panel-renesas-r69328.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + 3 + #include <linux/array_size.h> 4 + #include <linux/delay.h> 5 + #include <linux/err.h> 6 + #include <linux/gpio/consumer.h> 7 + #include <linux/mod_devicetable.h> 8 + #include <linux/module.h> 9 + #include <linux/property.h> 10 + #include <linux/regulator/consumer.h> 11 + 12 + #include <video/mipi_display.h> 13 + 14 + #include <drm/drm_mipi_dsi.h> 15 + #include <drm/drm_modes.h> 16 + #include <drm/drm_panel.h> 17 + 18 + #define R69328_MACP 0xb0 /* Manufacturer Access CMD Protect */ 19 + #define R69328_MACP_ON 0x03 20 + #define R69328_MACP_OFF 0x04 21 + 22 + #define R69328_GAMMA_SET_A 0xc8 /* Gamma Setting A */ 23 + #define R69328_GAMMA_SET_B 0xc9 /* Gamma Setting B */ 24 + #define R69328_GAMMA_SET_C 0xca /* Gamma Setting C */ 25 + 26 + #define R69328_POWER_SET 0xd1 27 + 28 + struct renesas_r69328 { 29 + struct drm_panel panel; 30 + struct mipi_dsi_device *dsi; 31 + 32 + struct regulator *vdd_supply; 33 + struct regulator *vddio_supply; 34 + struct gpio_desc *reset_gpio; 35 + 36 + bool prepared; 37 + }; 38 + 39 + static inline struct renesas_r69328 *to_renesas_r69328(struct drm_panel *panel) 40 + { 41 + return container_of(panel, struct renesas_r69328, panel); 42 + } 43 + 44 + static void renesas_r69328_reset(struct renesas_r69328 *priv) 45 + { 46 + gpiod_set_value_cansleep(priv->reset_gpio, 1); 47 + usleep_range(10000, 11000); 48 + gpiod_set_value_cansleep(priv->reset_gpio, 0); 49 + usleep_range(2000, 3000); 50 + } 51 + 52 + static int renesas_r69328_prepare(struct drm_panel *panel) 53 + { 54 + struct renesas_r69328 *priv = to_renesas_r69328(panel); 55 + struct device *dev = &priv->dsi->dev; 56 + int ret; 57 + 58 + if (priv->prepared) 59 + return 0; 60 + 61 + ret = regulator_enable(priv->vdd_supply); 62 + if (ret) { 63 + dev_err(dev, "failed to enable vdd power supply\n"); 64 + return ret; 65 + } 66 + 67 + usleep_range(10000, 11000); 68 + 69 + ret = regulator_enable(priv->vddio_supply); 70 + if (ret < 0) { 71 + dev_err(dev, "failed to enable vddio power supply\n"); 72 + return ret; 73 + } 74 + 75 + usleep_range(10000, 11000); 76 + 77 + renesas_r69328_reset(priv); 78 + 79 + priv->prepared = true; 80 + return 0; 81 + } 82 + 83 + static int renesas_r69328_enable(struct drm_panel *panel) 84 + { 85 + struct renesas_r69328 *priv = to_renesas_r69328(panel); 86 + struct mipi_dsi_multi_context ctx = { .dsi = priv->dsi }; 87 + 88 + /* Set address mode */ 89 + mipi_dsi_dcs_write_seq_multi(&ctx, MIPI_DCS_SET_ADDRESS_MODE, 0x00); 90 + mipi_dsi_dcs_set_pixel_format_multi(&ctx, MIPI_DCS_PIXEL_FMT_24BIT << 4); 91 + mipi_dsi_dcs_exit_sleep_mode_multi(&ctx); 92 + 93 + mipi_dsi_msleep(&ctx, 100); 94 + 95 + /* MACP Off */ 96 + mipi_dsi_generic_write_seq_multi(&ctx, R69328_MACP, R69328_MACP_OFF); 97 + 98 + mipi_dsi_generic_write_seq_multi(&ctx, R69328_POWER_SET, 0x14, 0x1d, 99 + 0x21, 0x67, 0x11, 0x9a); 100 + 101 + mipi_dsi_generic_write_seq_multi(&ctx, R69328_GAMMA_SET_A, 0x00, 0x1a, 102 + 0x20, 0x28, 0x25, 0x24, 0x26, 0x15, 0x13, 103 + 0x11, 0x18, 0x1e, 0x1c, 0x00, 0x00, 0x1a, 104 + 0x20, 0x28, 0x25, 0x24, 0x26, 0x15, 0x13, 105 + 0x11, 0x18, 0x1e, 0x1c, 0x00); 106 + 107 + mipi_dsi_generic_write_seq_multi(&ctx, R69328_GAMMA_SET_B, 0x00, 0x1a, 108 + 0x20, 0x28, 0x25, 0x24, 0x26, 0x15, 0x13, 109 + 0x11, 0x18, 0x1e, 0x1c, 0x00, 0x00, 0x1a, 110 + 0x20, 0x28, 0x25, 0x24, 0x26, 0x15, 0x13, 111 + 0x11, 0x18, 0x1e, 0x1c, 0x00); 112 + 113 + mipi_dsi_generic_write_seq_multi(&ctx, R69328_GAMMA_SET_C, 0x00, 0x1a, 114 + 0x20, 0x28, 0x25, 0x24, 0x26, 0x15, 0x13, 115 + 0x11, 0x18, 0x1e, 0x1c, 0x00, 0x00, 0x1a, 116 + 0x20, 0x28, 0x25, 0x24, 0x26, 0x15, 0x13, 117 + 0x11, 0x18, 0x1e, 0x1c, 0x00); 118 + 119 + /* MACP On */ 120 + mipi_dsi_generic_write_seq_multi(&ctx, R69328_MACP, R69328_MACP_ON); 121 + 122 + mipi_dsi_dcs_set_display_on_multi(&ctx); 123 + mipi_dsi_msleep(&ctx, 50); 124 + 125 + return 0; 126 + } 127 + 128 + static int renesas_r69328_disable(struct drm_panel *panel) 129 + { 130 + struct renesas_r69328 *priv = to_renesas_r69328(panel); 131 + struct mipi_dsi_multi_context ctx = { .dsi = priv->dsi }; 132 + 133 + mipi_dsi_dcs_set_display_off_multi(&ctx); 134 + mipi_dsi_msleep(&ctx, 60); 135 + mipi_dsi_dcs_enter_sleep_mode_multi(&ctx); 136 + 137 + return 0; 138 + } 139 + 140 + static int renesas_r69328_unprepare(struct drm_panel *panel) 141 + { 142 + struct renesas_r69328 *priv = to_renesas_r69328(panel); 143 + 144 + if (!priv->prepared) 145 + return 0; 146 + 147 + gpiod_set_value_cansleep(priv->reset_gpio, 1); 148 + 149 + usleep_range(5000, 6000); 150 + 151 + regulator_disable(priv->vddio_supply); 152 + regulator_disable(priv->vdd_supply); 153 + 154 + priv->prepared = false; 155 + return 0; 156 + } 157 + 158 + static const struct drm_display_mode renesas_r69328_mode = { 159 + .clock = (720 + 92 + 62 + 4) * (1280 + 6 + 3 + 1) * 60 / 1000, 160 + .hdisplay = 720, 161 + .hsync_start = 720 + 92, 162 + .hsync_end = 720 + 92 + 62, 163 + .htotal = 720 + 92 + 62 + 4, 164 + .vdisplay = 1280, 165 + .vsync_start = 1280 + 6, 166 + .vsync_end = 1280 + 6 + 3, 167 + .vtotal = 1280 + 6 + 3 + 1, 168 + .width_mm = 59, 169 + .height_mm = 105, 170 + }; 171 + 172 + static int renesas_r69328_get_modes(struct drm_panel *panel, 173 + struct drm_connector *connector) 174 + { 175 + struct drm_display_mode *mode; 176 + 177 + mode = drm_mode_duplicate(connector->dev, &renesas_r69328_mode); 178 + if (!mode) 179 + return -ENOMEM; 180 + 181 + drm_mode_set_name(mode); 182 + 183 + mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; 184 + connector->display_info.width_mm = mode->width_mm; 185 + connector->display_info.height_mm = mode->height_mm; 186 + drm_mode_probed_add(connector, mode); 187 + 188 + return 1; 189 + } 190 + 191 + static const struct drm_panel_funcs renesas_r69328_panel_funcs = { 192 + .prepare = renesas_r69328_prepare, 193 + .enable = renesas_r69328_enable, 194 + .disable = renesas_r69328_disable, 195 + .unprepare = renesas_r69328_unprepare, 196 + .get_modes = renesas_r69328_get_modes, 197 + }; 198 + 199 + static int renesas_r69328_probe(struct mipi_dsi_device *dsi) 200 + { 201 + struct device *dev = &dsi->dev; 202 + struct renesas_r69328 *priv; 203 + int ret; 204 + 205 + priv = devm_drm_panel_alloc(dev, struct renesas_r69328, panel, 206 + &renesas_r69328_panel_funcs, 207 + DRM_MODE_CONNECTOR_DSI); 208 + if (IS_ERR(priv)) 209 + return PTR_ERR(priv); 210 + 211 + priv->vdd_supply = devm_regulator_get(dev, "vdd"); 212 + if (IS_ERR(priv->vdd_supply)) 213 + return dev_err_probe(dev, PTR_ERR(priv->vdd_supply), 214 + "Failed to get vdd-supply\n"); 215 + 216 + priv->vddio_supply = devm_regulator_get(dev, "vddio"); 217 + if (IS_ERR(priv->vddio_supply)) 218 + return dev_err_probe(dev, PTR_ERR(priv->vddio_supply), 219 + "Failed to get vddio-supply\n"); 220 + 221 + priv->reset_gpio = devm_gpiod_get_optional(dev, "reset", 222 + GPIOD_OUT_LOW); 223 + if (IS_ERR(priv->reset_gpio)) 224 + return dev_err_probe(dev, PTR_ERR(priv->reset_gpio), 225 + "Failed to get reset-gpios\n"); 226 + 227 + priv->dsi = dsi; 228 + mipi_dsi_set_drvdata(dsi, priv); 229 + 230 + dsi->lanes = 4; 231 + dsi->format = MIPI_DSI_FMT_RGB888; 232 + dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | 233 + MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM; 234 + 235 + ret = drm_panel_of_backlight(&priv->panel); 236 + if (ret) 237 + return dev_err_probe(dev, ret, "Failed to get backlight\n"); 238 + 239 + drm_panel_add(&priv->panel); 240 + 241 + ret = mipi_dsi_attach(dsi); 242 + if (ret) { 243 + drm_panel_remove(&priv->panel); 244 + return dev_err_probe(dev, ret, "Failed to attach to DSI host\n"); 245 + } 246 + 247 + return 0; 248 + } 249 + 250 + static void renesas_r69328_remove(struct mipi_dsi_device *dsi) 251 + { 252 + struct renesas_r69328 *priv = mipi_dsi_get_drvdata(dsi); 253 + int ret; 254 + 255 + ret = mipi_dsi_detach(dsi); 256 + if (ret) 257 + dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret); 258 + 259 + drm_panel_remove(&priv->panel); 260 + } 261 + 262 + static const struct of_device_id renesas_r69328_of_match[] = { 263 + { .compatible = "jdi,dx12d100vm0eaa" }, 264 + { /* sentinel */ } 265 + }; 266 + MODULE_DEVICE_TABLE(of, renesas_r69328_of_match); 267 + 268 + static struct mipi_dsi_driver renesas_r69328_driver = { 269 + .probe = renesas_r69328_probe, 270 + .remove = renesas_r69328_remove, 271 + .driver = { 272 + .name = "panel-renesas-r69328", 273 + .of_match_table = renesas_r69328_of_match, 274 + }, 275 + }; 276 + module_mipi_dsi_driver(renesas_r69328_driver); 277 + 278 + MODULE_AUTHOR("Maxim Schwalm <maxim.schwalm@gmail.com>"); 279 + MODULE_AUTHOR("Svyatoslav Ryhel <clamor95@gmail.com>"); 280 + MODULE_DESCRIPTION("Renesas R69328-based panel driver"); 281 + MODULE_LICENSE("GPL");