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

drm/panel: Add Visionox G2647FB105 panel driver

Add the driver for Visionox G2647FB105 6.47" FHD Plus CMD mode AMOLED panel
support found in:
- Xiaomi Mi Note 10 / CC9 Pro (sm7150-xiaomi-tucana)
- Xiaomi Mi Note 10 Lite (sm7150-xiaomi-toco)

Signed-off-by: Alexander Baransky <sanyapilot496@gmail.com>
Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>
Link: https://lore.kernel.org/r/20250414172637.197792-3-sanyapilot496@gmail.com
Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
Link: https://lore.kernel.org/r/20250414172637.197792-3-sanyapilot496@gmail.com

authored by

Alexander Baransky and committed by
Neil Armstrong
3d55aebe 7a5d0cbd

+290
+9
drivers/gpu/drm/panel/Kconfig
··· 1007 1007 Say Y here if you want to enable support for Truly NT35597 WQXGA Dual DSI 1008 1008 Video Mode panel 1009 1009 1010 + config DRM_PANEL_VISIONOX_G2647FB105 1011 + tristate "Visionox G2647FB105" 1012 + depends on OF 1013 + depends on DRM_MIPI_DSI 1014 + depends on BACKLIGHT_CLASS_DEVICE 1015 + help 1016 + Say Y here if you want to enable support for the Visionox 1017 + G2647FB105 (2340x1080@60Hz) AMOLED DSI cmd mode panel. 1018 + 1010 1019 config DRM_PANEL_VISIONOX_R66451 1011 1020 tristate "Visionox R66451" 1012 1021 depends on OF
+1
drivers/gpu/drm/panel/Makefile
··· 102 102 obj-$(CONFIG_DRM_PANEL_TPO_TD043MTEA1) += panel-tpo-td043mtea1.o 103 103 obj-$(CONFIG_DRM_PANEL_TPO_TPG110) += panel-tpo-tpg110.o 104 104 obj-$(CONFIG_DRM_PANEL_TRULY_NT35597_WQXGA) += panel-truly-nt35597.o 105 + obj-$(CONFIG_DRM_PANEL_VISIONOX_G2647FB105) += panel-visionox-g2647fb105.o 105 106 obj-$(CONFIG_DRM_PANEL_VISIONOX_RM69299) += panel-visionox-rm69299.o 106 107 obj-$(CONFIG_DRM_PANEL_VISIONOX_RM692E5) += panel-visionox-rm692e5.o 107 108 obj-$(CONFIG_DRM_PANEL_VISIONOX_VTDR6130) += panel-visionox-vtdr6130.o
+280
drivers/gpu/drm/panel/panel-visionox-g2647fb105.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Generated with linux-mdss-dsi-panel-driver-generator from vendor device tree: 4 + * Copyright (c) 2013, The Linux Foundation. All rights reserved. 5 + * Copyright (c) 2025, Alexander Baransky <sanyapilot496@gmail.com> 6 + */ 7 + 8 + #include <linux/backlight.h> 9 + #include <linux/delay.h> 10 + #include <linux/gpio/consumer.h> 11 + #include <linux/module.h> 12 + #include <linux/of.h> 13 + #include <linux/regulator/consumer.h> 14 + 15 + #include <drm/drm_mipi_dsi.h> 16 + #include <drm/drm_modes.h> 17 + #include <drm/drm_panel.h> 18 + 19 + struct visionox_g2647fb105 { 20 + struct drm_panel panel; 21 + struct mipi_dsi_device *dsi; 22 + struct gpio_desc *reset_gpio; 23 + struct regulator_bulk_data *supplies; 24 + }; 25 + 26 + static const struct regulator_bulk_data visionox_g2647fb105_supplies[] = { 27 + { .supply = "vdd3p3" }, 28 + { .supply = "vddio" }, 29 + { .supply = "vsn" }, 30 + { .supply = "vsp" }, 31 + }; 32 + 33 + static inline 34 + struct visionox_g2647fb105 *to_visionox_g2647fb105(struct drm_panel *panel) 35 + { 36 + return container_of(panel, struct visionox_g2647fb105, panel); 37 + } 38 + 39 + static void visionox_g2647fb105_reset(struct visionox_g2647fb105 *ctx) 40 + { 41 + gpiod_set_value_cansleep(ctx->reset_gpio, 1); 42 + usleep_range(1000, 2000); 43 + gpiod_set_value_cansleep(ctx->reset_gpio, 0); 44 + usleep_range(10000, 11000); 45 + } 46 + 47 + static int visionox_g2647fb105_on(struct visionox_g2647fb105 *ctx) 48 + { 49 + struct mipi_dsi_device *dsi = ctx->dsi; 50 + struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi }; 51 + 52 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x70, 0x04); 53 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfe, 0x40); 54 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4d, 0x32); 55 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfe, 0x40); 56 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xbe, 0x17); 57 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xbf, 0xbb); 58 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xc0, 0xdd); 59 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xc1, 0xff); 60 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfe, 0xd0); 61 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x03, 0x24); 62 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x04, 0x03); 63 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfe, 0x00); 64 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xc2, 0x08); 65 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfe, 0x00); 66 + 67 + mipi_dsi_dcs_set_tear_on_multi(&dsi_ctx, MIPI_DSI_DCS_TEAR_MODE_VBLANK); 68 + mipi_dsi_dcs_set_display_brightness_multi(&dsi_ctx, 0x0000); 69 + mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx); 70 + mipi_dsi_msleep(&dsi_ctx, 100); 71 + 72 + mipi_dsi_dcs_set_display_on_multi(&dsi_ctx); 73 + 74 + return dsi_ctx.accum_err; 75 + } 76 + 77 + static int visionox_g2647fb105_off(struct visionox_g2647fb105 *ctx) 78 + { 79 + struct mipi_dsi_device *dsi = ctx->dsi; 80 + struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi }; 81 + 82 + mipi_dsi_dcs_set_display_off_multi(&dsi_ctx); 83 + mipi_dsi_msleep(&dsi_ctx, 50); 84 + 85 + mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx); 86 + mipi_dsi_msleep(&dsi_ctx, 20); 87 + 88 + return dsi_ctx.accum_err; 89 + } 90 + 91 + static int visionox_g2647fb105_prepare(struct drm_panel *panel) 92 + { 93 + struct visionox_g2647fb105 *ctx = to_visionox_g2647fb105(panel); 94 + struct device *dev = &ctx->dsi->dev; 95 + int ret; 96 + 97 + ret = regulator_bulk_enable(ARRAY_SIZE(visionox_g2647fb105_supplies), ctx->supplies); 98 + if (ret < 0) { 99 + dev_err(dev, "Failed to enable regulators: %d\n", ret); 100 + return ret; 101 + } 102 + 103 + visionox_g2647fb105_reset(ctx); 104 + 105 + ret = visionox_g2647fb105_on(ctx); 106 + if (ret < 0) { 107 + dev_err(dev, "Failed to initialize panel: %d\n", ret); 108 + return ret; 109 + } 110 + 111 + return 0; 112 + } 113 + 114 + static int visionox_g2647fb105_unprepare(struct drm_panel *panel) 115 + { 116 + struct visionox_g2647fb105 *ctx = to_visionox_g2647fb105(panel); 117 + struct device *dev = &ctx->dsi->dev; 118 + int ret; 119 + 120 + ret = visionox_g2647fb105_off(ctx); 121 + if (ret < 0) 122 + dev_err(dev, "Failed to un-initialize panel: %d\n", ret); 123 + 124 + gpiod_set_value_cansleep(ctx->reset_gpio, 1); 125 + regulator_bulk_disable(ARRAY_SIZE(visionox_g2647fb105_supplies), ctx->supplies); 126 + 127 + return 0; 128 + } 129 + 130 + static const struct drm_display_mode visionox_g2647fb105_mode = { 131 + .clock = (1080 + 28 + 4 + 36) * (2340 + 8 + 4 + 4) * 60 / 1000, 132 + .hdisplay = 1080, 133 + .hsync_start = 1080 + 28, 134 + .hsync_end = 1080 + 28 + 4, 135 + .htotal = 1080 + 28 + 4 + 36, 136 + .vdisplay = 2340, 137 + .vsync_start = 2340 + 8, 138 + .vsync_end = 2340 + 8 + 4, 139 + .vtotal = 2340 + 8 + 4 + 4, 140 + .width_mm = 69, 141 + .height_mm = 149, 142 + }; 143 + 144 + static int visionox_g2647fb105_get_modes(struct drm_panel *panel, 145 + struct drm_connector *connector) 146 + { 147 + struct drm_display_mode *mode; 148 + 149 + mode = drm_mode_duplicate(connector->dev, &visionox_g2647fb105_mode); 150 + if (!mode) 151 + return -ENOMEM; 152 + 153 + drm_mode_set_name(mode); 154 + 155 + mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; 156 + connector->display_info.width_mm = mode->width_mm; 157 + connector->display_info.height_mm = mode->height_mm; 158 + drm_mode_probed_add(connector, mode); 159 + 160 + return 1; 161 + } 162 + 163 + static const struct drm_panel_funcs visionox_g2647fb105_panel_funcs = { 164 + .prepare = visionox_g2647fb105_prepare, 165 + .unprepare = visionox_g2647fb105_unprepare, 166 + .get_modes = visionox_g2647fb105_get_modes, 167 + }; 168 + 169 + static int visionox_g2647fb105_bl_update_status(struct backlight_device *bl) 170 + { 171 + struct mipi_dsi_device *dsi = bl_get_data(bl); 172 + u16 brightness = backlight_get_brightness(bl); 173 + int ret; 174 + 175 + dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; 176 + 177 + ret = mipi_dsi_dcs_set_display_brightness_large(dsi, brightness); 178 + if (ret < 0) 179 + return ret; 180 + 181 + dsi->mode_flags |= MIPI_DSI_MODE_LPM; 182 + 183 + return 0; 184 + } 185 + 186 + static const struct backlight_ops visionox_g2647fb105_bl_ops = { 187 + .update_status = visionox_g2647fb105_bl_update_status, 188 + }; 189 + 190 + static struct backlight_device * 191 + visionox_g2647fb105_create_backlight(struct mipi_dsi_device *dsi) 192 + { 193 + struct device *dev = &dsi->dev; 194 + const struct backlight_properties props = { 195 + .type = BACKLIGHT_RAW, 196 + .brightness = 1023, 197 + .max_brightness = 2047, 198 + }; 199 + 200 + return devm_backlight_device_register(dev, dev_name(dev), dev, dsi, 201 + &visionox_g2647fb105_bl_ops, &props); 202 + } 203 + 204 + static int visionox_g2647fb105_probe(struct mipi_dsi_device *dsi) 205 + { 206 + struct device *dev = &dsi->dev; 207 + struct visionox_g2647fb105 *ctx; 208 + int ret; 209 + 210 + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); 211 + if (!ctx) 212 + return -ENOMEM; 213 + 214 + ret = devm_regulator_bulk_get_const(dev, 215 + ARRAY_SIZE(visionox_g2647fb105_supplies), 216 + visionox_g2647fb105_supplies, 217 + &ctx->supplies); 218 + if (ret < 0) 219 + return dev_err_probe(dev, ret, "Failed to get regulators\n"); 220 + 221 + ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); 222 + if (IS_ERR(ctx->reset_gpio)) 223 + return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), 224 + "Failed to get reset-gpios\n"); 225 + 226 + ctx->dsi = dsi; 227 + mipi_dsi_set_drvdata(dsi, ctx); 228 + 229 + dsi->lanes = 4; 230 + dsi->format = MIPI_DSI_FMT_RGB888; 231 + dsi->mode_flags = MIPI_DSI_MODE_VIDEO_BURST | 232 + MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM; 233 + 234 + ctx->panel.prepare_prev_first = true; 235 + 236 + drm_panel_init(&ctx->panel, dev, &visionox_g2647fb105_panel_funcs, 237 + DRM_MODE_CONNECTOR_DSI); 238 + ctx->panel.prepare_prev_first = true; 239 + 240 + ctx->panel.backlight = visionox_g2647fb105_create_backlight(dsi); 241 + if (IS_ERR(ctx->panel.backlight)) 242 + return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight), 243 + "Failed to create backlight\n"); 244 + 245 + drm_panel_add(&ctx->panel); 246 + 247 + ret = devm_mipi_dsi_attach(dev, dsi); 248 + if (ret < 0) { 249 + drm_panel_remove(&ctx->panel); 250 + return dev_err_probe(dev, ret, "Failed to attach to DSI host\n"); 251 + } 252 + 253 + return 0; 254 + } 255 + 256 + static void visionox_g2647fb105_remove(struct mipi_dsi_device *dsi) 257 + { 258 + struct visionox_g2647fb105 *ctx = mipi_dsi_get_drvdata(dsi); 259 + drm_panel_remove(&ctx->panel); 260 + } 261 + 262 + static const struct of_device_id visionox_g2647fb105_of_match[] = { 263 + { .compatible = "visionox,g2647fb105" }, 264 + { /* sentinel */ } 265 + }; 266 + MODULE_DEVICE_TABLE(of, visionox_g2647fb105_of_match); 267 + 268 + static struct mipi_dsi_driver visionox_g2647fb105_driver = { 269 + .probe = visionox_g2647fb105_probe, 270 + .remove = visionox_g2647fb105_remove, 271 + .driver = { 272 + .name = "panel-visionox-g2647fb105", 273 + .of_match_table = visionox_g2647fb105_of_match, 274 + }, 275 + }; 276 + module_mipi_dsi_driver(visionox_g2647fb105_driver); 277 + 278 + MODULE_AUTHOR("Alexander Baransky <sanyapilot496@gmail.com>"); 279 + MODULE_DESCRIPTION("DRM driver for Visionox G2647FB105 AMOLED DSI panel"); 280 + MODULE_LICENSE("GPL");