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 R61307 based MIPI DSI panel

R61307 is liquid crystal driver for high-definition amorphous silicon
(a-Si) panels and is ideal for tablets and smartphones.

Supported compatibles are:
- hit,tx13d100vm0eaa
- koe,tx13d100vm0eaa

Signed-off-by: Svyatoslav Ryhel <clamor95@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-3-clamor95@gmail.com
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>

authored by

Svyatoslav Ryhel and committed by
Dmitry Baryshkov
cb6c01ea 43adabbe

+339
+13
drivers/gpu/drm/panel/Kconfig
··· 645 645 This panel controller can be found in the Lenovo Xiaoxin Pad Pro 2021 646 646 in combination with an EDO OLED panel. 647 647 648 + config DRM_PANEL_RENESAS_R61307 649 + tristate "Renesas R61307 DSI video mode panel" 650 + depends on OF 651 + depends on DRM_MIPI_DSI 652 + depends on BACKLIGHT_CLASS_DEVICE 653 + help 654 + Say Y here if you want to enable support for KOE tx13d100vm0eaa 655 + IPS-LCD module with Renesas R69328 IC. The panel has a 1024x768 656 + resolution and uses 24 bit RGB per pixel. 657 + 658 + This panel controller can be found in LG Optimus Vu P895 smartphone 659 + in combination with LCD panel. 660 + 648 661 config DRM_PANEL_RONBO_RB070D30 649 662 tristate "Ronbo Electronics RB070D30 panel" 650 663 depends on OF
+1
drivers/gpu/drm/panel/Makefile
··· 65 65 obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM68200) += panel-raydium-rm68200.o 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 + obj-$(CONFIG_DRM_PANEL_RENESAS_R61307) += panel-renesas-r61307.o 68 69 obj-$(CONFIG_DRM_PANEL_RONBO_RB070D30) += panel-ronbo-rb070d30.o 69 70 obj-$(CONFIG_DRM_PANEL_SAMSUNG_AMS581VF01) += panel-samsung-ams581vf01.o 70 71 obj-$(CONFIG_DRM_PANEL_SAMSUNG_AMS639RQ08) += panel-samsung-ams639rq08.o
+325
drivers/gpu/drm/panel/panel-renesas-r61307.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 R61307_MACP 0xb0 /* Manufacturer CMD Protect */ 19 + #define R61307_MACP_ON 0x03 20 + #define R61307_MACP_OFF 0x04 21 + 22 + #define R61307_INVERSION 0xc1 23 + #define R61307_GAMMA_SET_A 0xc8 /* Gamma Setting A */ 24 + #define R61307_GAMMA_SET_B 0xc9 /* Gamma Setting B */ 25 + #define R61307_GAMMA_SET_C 0xca /* Gamma Setting C */ 26 + #define R61307_CONTRAST_SET 0xcc 27 + 28 + struct renesas_r61307 { 29 + struct drm_panel panel; 30 + struct mipi_dsi_device *dsi; 31 + 32 + struct regulator *vcc_supply; 33 + struct regulator *iovcc_supply; 34 + 35 + struct gpio_desc *reset_gpio; 36 + 37 + bool prepared; 38 + 39 + bool dig_cont_adj; 40 + bool inversion; 41 + u32 gamma; 42 + }; 43 + 44 + static const u8 gamma_setting[][25] = { 45 + { /* sentinel */ }, 46 + { 47 + R61307_GAMMA_SET_A, 48 + 0x00, 0x06, 0x0a, 0x0f, 49 + 0x14, 0x1f, 0x1f, 0x17, 50 + 0x12, 0x0c, 0x09, 0x06, 51 + 0x00, 0x06, 0x0a, 0x0f, 52 + 0x14, 0x1f, 0x1f, 0x17, 53 + 0x12, 0x0c, 0x09, 0x06 54 + }, 55 + { 56 + R61307_GAMMA_SET_A, 57 + 0x00, 0x05, 0x0b, 0x0f, 58 + 0x11, 0x1d, 0x20, 0x18, 59 + 0x18, 0x09, 0x07, 0x06, 60 + 0x00, 0x05, 0x0b, 0x0f, 61 + 0x11, 0x1d, 0x20, 0x18, 62 + 0x18, 0x09, 0x07, 0x06 63 + }, 64 + { 65 + R61307_GAMMA_SET_A, 66 + 0x0b, 0x0d, 0x10, 0x14, 67 + 0x13, 0x1d, 0x20, 0x18, 68 + 0x12, 0x09, 0x07, 0x06, 69 + 0x0a, 0x0c, 0x10, 0x14, 70 + 0x13, 0x1d, 0x20, 0x18, 71 + 0x12, 0x09, 0x07, 0x06 72 + }, 73 + }; 74 + 75 + static inline struct renesas_r61307 *to_renesas_r61307(struct drm_panel *panel) 76 + { 77 + return container_of(panel, struct renesas_r61307, panel); 78 + } 79 + 80 + static void renesas_r61307_reset(struct renesas_r61307 *priv) 81 + { 82 + gpiod_set_value_cansleep(priv->reset_gpio, 1); 83 + usleep_range(10000, 11000); 84 + gpiod_set_value_cansleep(priv->reset_gpio, 0); 85 + usleep_range(2000, 3000); 86 + } 87 + 88 + static int renesas_r61307_prepare(struct drm_panel *panel) 89 + { 90 + struct renesas_r61307 *priv = to_renesas_r61307(panel); 91 + struct device *dev = &priv->dsi->dev; 92 + int ret; 93 + 94 + if (priv->prepared) 95 + return 0; 96 + 97 + ret = regulator_enable(priv->vcc_supply); 98 + if (ret) { 99 + dev_err(dev, "failed to enable vcc power supply\n"); 100 + return ret; 101 + } 102 + 103 + usleep_range(2000, 3000); 104 + 105 + ret = regulator_enable(priv->iovcc_supply); 106 + if (ret) { 107 + dev_err(dev, "failed to enable iovcc power supply\n"); 108 + return ret; 109 + } 110 + 111 + usleep_range(2000, 3000); 112 + 113 + renesas_r61307_reset(priv); 114 + 115 + priv->prepared = true; 116 + return 0; 117 + } 118 + 119 + static int renesas_r61307_enable(struct drm_panel *panel) 120 + { 121 + struct renesas_r61307 *priv = to_renesas_r61307(panel); 122 + struct mipi_dsi_multi_context ctx = { .dsi = priv->dsi }; 123 + 124 + mipi_dsi_dcs_exit_sleep_mode_multi(&ctx); 125 + mipi_dsi_msleep(&ctx, 80); 126 + 127 + mipi_dsi_dcs_write_seq_multi(&ctx, MIPI_DCS_SET_ADDRESS_MODE, 0x00); 128 + mipi_dsi_msleep(&ctx, 20); 129 + 130 + mipi_dsi_dcs_set_pixel_format_multi(&ctx, MIPI_DCS_PIXEL_FMT_24BIT << 4); 131 + 132 + /* MACP Off */ 133 + mipi_dsi_generic_write_seq_multi(&ctx, R61307_MACP, R61307_MACP_OFF); 134 + 135 + if (priv->dig_cont_adj) 136 + mipi_dsi_generic_write_seq_multi(&ctx, R61307_CONTRAST_SET, 137 + 0xdc, 0xb4, 0xff); 138 + 139 + if (priv->gamma) 140 + mipi_dsi_generic_write_multi(&ctx, gamma_setting[priv->gamma], 141 + sizeof(gamma_setting[priv->gamma])); 142 + 143 + if (priv->inversion) 144 + mipi_dsi_generic_write_seq_multi(&ctx, R61307_INVERSION, 145 + 0x00, 0x50, 0x03, 0x22, 146 + 0x16, 0x06, 0x60, 0x11); 147 + else 148 + mipi_dsi_generic_write_seq_multi(&ctx, R61307_INVERSION, 149 + 0x00, 0x10, 0x03, 0x22, 150 + 0x16, 0x06, 0x60, 0x01); 151 + 152 + /* MACP On */ 153 + mipi_dsi_generic_write_seq_multi(&ctx, R61307_MACP, R61307_MACP_ON); 154 + 155 + mipi_dsi_dcs_set_display_on_multi(&ctx); 156 + mipi_dsi_msleep(&ctx, 50); 157 + 158 + return 0; 159 + } 160 + 161 + static int renesas_r61307_disable(struct drm_panel *panel) 162 + { 163 + struct renesas_r61307 *priv = to_renesas_r61307(panel); 164 + struct mipi_dsi_multi_context ctx = { .dsi = priv->dsi }; 165 + 166 + mipi_dsi_dcs_set_display_off_multi(&ctx); 167 + mipi_dsi_msleep(&ctx, 100); 168 + mipi_dsi_dcs_enter_sleep_mode_multi(&ctx); 169 + 170 + return 0; 171 + } 172 + 173 + static int renesas_r61307_unprepare(struct drm_panel *panel) 174 + { 175 + struct renesas_r61307 *priv = to_renesas_r61307(panel); 176 + 177 + if (!priv->prepared) 178 + return 0; 179 + 180 + usleep_range(10000, 11000); 181 + 182 + gpiod_set_value_cansleep(priv->reset_gpio, 1); 183 + usleep_range(5000, 6000); 184 + 185 + regulator_disable(priv->iovcc_supply); 186 + usleep_range(2000, 3000); 187 + regulator_disable(priv->vcc_supply); 188 + 189 + priv->prepared = false; 190 + return 0; 191 + } 192 + 193 + static const struct drm_display_mode renesas_r61307_mode = { 194 + .clock = (768 + 116 + 81 + 5) * (1024 + 24 + 8 + 2) * 60 / 1000, 195 + .hdisplay = 768, 196 + .hsync_start = 768 + 116, 197 + .hsync_end = 768 + 116 + 81, 198 + .htotal = 768 + 116 + 81 + 5, 199 + .vdisplay = 1024, 200 + .vsync_start = 1024 + 24, 201 + .vsync_end = 1024 + 24 + 8, 202 + .vtotal = 1024 + 24 + 8 + 2, 203 + .width_mm = 76, 204 + .height_mm = 101, 205 + }; 206 + 207 + static int renesas_r61307_get_modes(struct drm_panel *panel, 208 + struct drm_connector *connector) 209 + { 210 + struct drm_display_mode *mode; 211 + 212 + mode = drm_mode_duplicate(connector->dev, &renesas_r61307_mode); 213 + if (!mode) 214 + return -ENOMEM; 215 + 216 + drm_mode_set_name(mode); 217 + 218 + mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; 219 + connector->display_info.width_mm = mode->width_mm; 220 + connector->display_info.height_mm = mode->height_mm; 221 + drm_mode_probed_add(connector, mode); 222 + 223 + return 1; 224 + } 225 + 226 + static const struct drm_panel_funcs renesas_r61307_panel_funcs = { 227 + .prepare = renesas_r61307_prepare, 228 + .enable = renesas_r61307_enable, 229 + .disable = renesas_r61307_disable, 230 + .unprepare = renesas_r61307_unprepare, 231 + .get_modes = renesas_r61307_get_modes, 232 + }; 233 + 234 + static int renesas_r61307_probe(struct mipi_dsi_device *dsi) 235 + { 236 + struct device *dev = &dsi->dev; 237 + struct renesas_r61307 *priv; 238 + int ret; 239 + 240 + priv = devm_drm_panel_alloc(dev, struct renesas_r61307, panel, 241 + &renesas_r61307_panel_funcs, 242 + DRM_MODE_CONNECTOR_DSI); 243 + if (IS_ERR(priv)) 244 + return PTR_ERR(priv); 245 + 246 + priv->vcc_supply = devm_regulator_get(dev, "vcc"); 247 + if (IS_ERR(priv->vcc_supply)) 248 + return dev_err_probe(dev, PTR_ERR(priv->vcc_supply), 249 + "Failed to get vcc-supply\n"); 250 + 251 + priv->iovcc_supply = devm_regulator_get(dev, "iovcc"); 252 + if (IS_ERR(priv->iovcc_supply)) 253 + return dev_err_probe(dev, PTR_ERR(priv->iovcc_supply), 254 + "Failed to get iovcc-supply\n"); 255 + 256 + priv->reset_gpio = devm_gpiod_get_optional(dev, "reset", 257 + GPIOD_OUT_HIGH); 258 + if (IS_ERR(priv->reset_gpio)) 259 + return dev_err_probe(dev, PTR_ERR(priv->reset_gpio), 260 + "Failed to get reset gpios\n"); 261 + 262 + if (device_property_read_bool(dev, "renesas,inversion")) 263 + priv->inversion = true; 264 + 265 + if (device_property_read_bool(dev, "renesas,contrast")) 266 + priv->dig_cont_adj = true; 267 + 268 + priv->gamma = 0; 269 + device_property_read_u32(dev, "renesas,gamma", &priv->gamma); 270 + 271 + priv->dsi = dsi; 272 + mipi_dsi_set_drvdata(dsi, priv); 273 + 274 + dsi->lanes = 4; 275 + dsi->format = MIPI_DSI_FMT_RGB888; 276 + dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | 277 + MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM; 278 + 279 + ret = drm_panel_of_backlight(&priv->panel); 280 + if (ret) 281 + return dev_err_probe(dev, ret, "Failed to get backlight\n"); 282 + 283 + drm_panel_add(&priv->panel); 284 + 285 + ret = mipi_dsi_attach(dsi); 286 + if (ret) { 287 + drm_panel_remove(&priv->panel); 288 + return dev_err_probe(dev, ret, "Failed to attach to DSI host\n"); 289 + } 290 + 291 + return 0; 292 + } 293 + 294 + static void renesas_r61307_remove(struct mipi_dsi_device *dsi) 295 + { 296 + struct renesas_r61307 *priv = mipi_dsi_get_drvdata(dsi); 297 + int ret; 298 + 299 + ret = mipi_dsi_detach(dsi); 300 + if (ret) 301 + dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret); 302 + 303 + drm_panel_remove(&priv->panel); 304 + } 305 + 306 + static const struct of_device_id renesas_r61307_of_match[] = { 307 + { .compatible = "hit,tx13d100vm0eaa" }, 308 + { .compatible = "koe,tx13d100vm0eaa" }, 309 + { /* sentinel */ } 310 + }; 311 + MODULE_DEVICE_TABLE(of, renesas_r61307_of_match); 312 + 313 + static struct mipi_dsi_driver renesas_r61307_driver = { 314 + .probe = renesas_r61307_probe, 315 + .remove = renesas_r61307_remove, 316 + .driver = { 317 + .name = "panel-renesas-r61307", 318 + .of_match_table = renesas_r61307_of_match, 319 + }, 320 + }; 321 + module_mipi_dsi_driver(renesas_r61307_driver); 322 + 323 + MODULE_AUTHOR("Svyatoslav Ryhel <clamor95@gmail.com>"); 324 + MODULE_DESCRIPTION("Renesas R61307-based panel driver"); 325 + MODULE_LICENSE("GPL");