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"); 281