1 //SPDX-License-Identifier: GPL-2.0-only 2 //Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. 3 4 #include <linux/backlight.h> 5 #include <linux/delay.h> 6 #include <linux/gpio/consumer.h> 7 #include <linux/module.h> 8 #include <linux/of.h> 9 #include <linux/regulator/consumer.h> 10 11 #include <drm/drm_mipi_dsi.h> 12 #include <drm/drm_probe_helper.h> 13 #include <drm/drm_modes.h> 14 #include <drm/drm_panel.h> 15 #include <drm/display/drm_dsc.h> 16 #include <drm/display/drm_dsc_helper.h> 17 18 #include <video/mipi_display.h> 19 20 struct visionox_r66451 { 21 struct drm_panel panel; 22 struct mipi_dsi_device *dsi; 23 struct gpio_desc *reset_gpio; 24 struct regulator_bulk_data supplies[2]; 25 }; 26 27 static inline struct visionox_r66451 *to_visionox_r66451(struct drm_panel *panel) 28 { 29 return container_of(panel, struct visionox_r66451, panel); 30 } 31 32 static void visionox_r66451_reset(struct visionox_r66451 *ctx) 33 { 34 gpiod_set_value_cansleep(ctx->reset_gpio, 0); 35 usleep_range(10000, 10100); 36 gpiod_set_value_cansleep(ctx->reset_gpio, 1); 37 usleep_range(10000, 10100); 38 gpiod_set_value_cansleep(ctx->reset_gpio, 0); 39 usleep_range(10000, 10100); 40 } 41 42 static int visionox_r66451_on(struct visionox_r66451 *ctx) 43 { 44 struct mipi_dsi_device *dsi = ctx->dsi; 45 struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi }; 46 47 dsi->mode_flags |= MIPI_DSI_MODE_LPM; 48 49 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb0, 0x00); 50 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xc2, 51 0x09, 0x24, 0x0c, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 52 0x09, 0x3c); 53 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xd7, 54 0x00, 0xb9, 0x3c, 0x00, 0x40, 0x04, 0x00, 0xa0, 0x0a, 55 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 56 0x3c, 0x00, 0x40, 0x04, 0x00, 0xa0, 0x0a); 57 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb0, 0x80); 58 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xde, 59 0x40, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 60 0x10, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x02, 0x00, 0x00); 61 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb0, 0x04); 62 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xe8, 0x00, 0x02); 63 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xe4, 0x00, 0x08); 64 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb0, 0x00); 65 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xc4, 66 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 67 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x32); 68 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xcf, 69 0x64, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 70 0x00, 0x0b, 0x77, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 71 0x02, 0x02, 0x02, 0x02, 0x02, 0x03); 72 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xd3, 73 0x45, 0x00, 0x00, 0x01, 0x13, 0x15, 0x00, 0x15, 0x07, 74 0x0f, 0x77, 0x77, 0x77, 0x37, 0xb2, 0x11, 0x00, 0xa0, 75 0x3c, 0x9c); 76 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xd7, 77 0x00, 0xb9, 0x34, 0x00, 0x40, 0x04, 0x00, 0xa0, 0x0a, 78 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 79 0x34, 0x00, 0x40, 0x04, 0x00, 0xa0, 0x0a); 80 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xd8, 81 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 82 0x3a, 0x00, 0x3a, 0x00, 0x3a, 0x00, 0x3a, 0x00, 0x3a, 83 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 84 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 85 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 86 0x00, 0x32, 0x00, 0x0a, 0x00, 0x22); 87 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xdf, 88 0x50, 0x42, 0x58, 0x81, 0x2d, 0x00, 0x00, 0x00, 0x00, 89 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 90 0x00, 0x00, 0x01, 0x0f, 0xff, 0xd4, 0x0e, 0x00, 0x00, 91 0x00, 0x00, 0x00, 0x00, 0x0f, 0x53, 0xf1, 0x00, 0x00, 92 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); 93 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf7, 0x01); 94 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb0, 0x80); 95 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xe4, 0x34, 0xb4, 0x00, 0x00, 0x00, 0x39, 96 0x04, 0x09, 0x34); 97 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xe6, 0x00); 98 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb0, 0x04); 99 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xdf, 0x50, 0x40); 100 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf3, 0x50, 0x00, 0x00, 0x00, 0x00); 101 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf2, 0x11); 102 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf3, 0x01, 0x00, 0x00, 0x00, 0x01); 103 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf4, 0x00, 0x02); 104 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf2, 0x19); 105 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xdf, 0x50, 0x42); 106 mipi_dsi_dcs_set_tear_on_multi(&dsi_ctx, MIPI_DSI_DCS_TEAR_MODE_VBLANK); 107 mipi_dsi_dcs_set_column_address_multi(&dsi_ctx, 0, 1080 - 1); 108 mipi_dsi_dcs_set_page_address_multi(&dsi_ctx, 0, 2340 - 1); 109 110 dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; 111 112 return dsi_ctx.accum_err; 113 } 114 115 static void visionox_r66451_off(struct visionox_r66451 *ctx) 116 { 117 ctx->dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; 118 } 119 120 static int visionox_r66451_prepare(struct drm_panel *panel) 121 { 122 struct visionox_r66451 *ctx = to_visionox_r66451(panel); 123 int ret; 124 125 ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), 126 ctx->supplies); 127 if (ret < 0) 128 return ret; 129 130 visionox_r66451_reset(ctx); 131 132 ret = visionox_r66451_on(ctx); 133 if (ret < 0) { 134 gpiod_set_value_cansleep(ctx->reset_gpio, 1); 135 regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); 136 return ret; 137 } 138 139 mipi_dsi_compression_mode(ctx->dsi, true); 140 141 return 0; 142 } 143 144 static int visionox_r66451_unprepare(struct drm_panel *panel) 145 { 146 struct visionox_r66451 *ctx = to_visionox_r66451(panel); 147 148 visionox_r66451_off(ctx); 149 150 gpiod_set_value_cansleep(ctx->reset_gpio, 1); 151 regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); 152 153 return 0; 154 } 155 156 static const struct drm_display_mode visionox_r66451_mode = { 157 .clock = 345830, 158 .hdisplay = 1080, 159 .hsync_start = 1175, 160 .hsync_end = 1176, 161 .htotal = 1216, 162 .vdisplay = 2340, 163 .vsync_start = 2365, 164 .vsync_end = 2366, 165 .vtotal = 2370, 166 .width_mm = 0, 167 .height_mm = 0, 168 .type = DRM_MODE_TYPE_DRIVER, 169 }; 170 171 static int visionox_r66451_enable(struct drm_panel *panel) 172 { 173 struct visionox_r66451 *ctx = to_visionox_r66451(panel); 174 struct mipi_dsi_device *dsi = ctx->dsi; 175 struct drm_dsc_picture_parameter_set pps; 176 struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi }; 177 178 if (!dsi->dsc) { 179 dev_err(&dsi->dev, "DSC not attached to DSI\n"); 180 return -ENODEV; 181 } 182 183 drm_dsc_pps_payload_pack(&pps, dsi->dsc); 184 mipi_dsi_picture_parameter_set_multi(&dsi_ctx, &pps); 185 186 mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx); 187 mipi_dsi_msleep(&dsi_ctx, 120); 188 189 mipi_dsi_dcs_set_display_on_multi(&dsi_ctx); 190 mipi_dsi_msleep(&dsi_ctx, 20); 191 192 return dsi_ctx.accum_err; 193 } 194 195 static int visionox_r66451_disable(struct drm_panel *panel) 196 { 197 struct visionox_r66451 *ctx = to_visionox_r66451(panel); 198 struct mipi_dsi_device *dsi = ctx->dsi; 199 struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi }; 200 201 mipi_dsi_dcs_set_display_off_multi(&dsi_ctx); 202 mipi_dsi_msleep(&dsi_ctx, 20); 203 204 mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx); 205 mipi_dsi_msleep(&dsi_ctx, 120); 206 207 return dsi_ctx.accum_err; 208 } 209 210 static int visionox_r66451_get_modes(struct drm_panel *panel, 211 struct drm_connector *connector) 212 { 213 drm_connector_helper_get_modes_fixed(connector, &visionox_r66451_mode); 214 return 1; 215 } 216 217 static const struct drm_panel_funcs visionox_r66451_funcs = { 218 .prepare = visionox_r66451_prepare, 219 .unprepare = visionox_r66451_unprepare, 220 .get_modes = visionox_r66451_get_modes, 221 .enable = visionox_r66451_enable, 222 .disable = visionox_r66451_disable, 223 }; 224 225 static int visionox_r66451_bl_update_status(struct backlight_device *bl) 226 { 227 struct mipi_dsi_device *dsi = bl_get_data(bl); 228 u16 brightness = backlight_get_brightness(bl); 229 230 return mipi_dsi_dcs_set_display_brightness(dsi, brightness); 231 } 232 233 static const struct backlight_ops visionox_r66451_bl_ops = { 234 .update_status = visionox_r66451_bl_update_status, 235 }; 236 237 static struct backlight_device * 238 visionox_r66451_create_backlight(struct mipi_dsi_device *dsi) 239 { 240 struct device *dev = &dsi->dev; 241 const struct backlight_properties props = { 242 .type = BACKLIGHT_RAW, 243 .brightness = 255, 244 .max_brightness = 4095, 245 }; 246 247 return devm_backlight_device_register(dev, dev_name(dev), dev, dsi, 248 &visionox_r66451_bl_ops, &props); 249 } 250 251 static int visionox_r66451_probe(struct mipi_dsi_device *dsi) 252 { 253 struct device *dev = &dsi->dev; 254 struct visionox_r66451 *ctx; 255 struct drm_dsc_config *dsc; 256 int ret = 0; 257 258 ctx = devm_drm_panel_alloc(dev, struct visionox_r66451, panel, 259 &visionox_r66451_funcs, 260 DRM_MODE_CONNECTOR_DSI); 261 if (IS_ERR(ctx)) 262 return PTR_ERR(ctx); 263 264 dsc = devm_kzalloc(dev, sizeof(*dsc), GFP_KERNEL); 265 if (!dsc) 266 return -ENOMEM; 267 268 /* Set DSC params */ 269 dsc->dsc_version_major = 0x1; 270 dsc->dsc_version_minor = 0x2; 271 272 dsc->slice_height = 20; 273 dsc->slice_width = 540; 274 dsc->slice_count = 2; 275 dsc->bits_per_component = 8; 276 dsc->bits_per_pixel = 8 << 4; 277 dsc->block_pred_enable = true; 278 279 dsi->dsc = dsc; 280 281 ctx->supplies[0].supply = "vddio"; 282 ctx->supplies[1].supply = "vdd"; 283 284 ret = devm_regulator_bulk_get(&dsi->dev, ARRAY_SIZE(ctx->supplies), 285 ctx->supplies); 286 287 if (ret < 0) 288 return ret; 289 290 ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); 291 if (IS_ERR(ctx->reset_gpio)) 292 return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), "Failed to get reset-gpios\n"); 293 294 ctx->dsi = dsi; 295 mipi_dsi_set_drvdata(dsi, ctx); 296 297 dsi->lanes = 4; 298 dsi->format = MIPI_DSI_FMT_RGB888; 299 dsi->mode_flags = MIPI_DSI_MODE_LPM | MIPI_DSI_CLOCK_NON_CONTINUOUS; 300 ctx->panel.prepare_prev_first = true; 301 302 ctx->panel.backlight = visionox_r66451_create_backlight(dsi); 303 if (IS_ERR(ctx->panel.backlight)) 304 return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight), 305 "Failed to create backlight\n"); 306 307 drm_panel_add(&ctx->panel); 308 309 ret = mipi_dsi_attach(dsi); 310 if (ret < 0) { 311 dev_err(dev, "Failed to attach to DSI host: %d\n", ret); 312 drm_panel_remove(&ctx->panel); 313 } 314 315 return ret; 316 } 317 318 static void visionox_r66451_remove(struct mipi_dsi_device *dsi) 319 { 320 struct visionox_r66451 *ctx = mipi_dsi_get_drvdata(dsi); 321 int ret; 322 323 ret = mipi_dsi_detach(dsi); 324 if (ret < 0) 325 dev_err(&dsi->dev, "Failed to detach DSI host: %d\n", ret); 326 327 drm_panel_remove(&ctx->panel); 328 } 329 330 static const struct of_device_id visionox_r66451_of_match[] = { 331 {.compatible = "visionox,r66451"}, 332 { /*sentinel*/ } 333 }; 334 MODULE_DEVICE_TABLE(of, visionox_r66451_of_match); 335 336 static struct mipi_dsi_driver visionox_r66451_driver = { 337 .probe = visionox_r66451_probe, 338 .remove = visionox_r66451_remove, 339 .driver = { 340 .name = "panel-visionox-r66451", 341 .of_match_table = visionox_r66451_of_match, 342 }, 343 }; 344 345 module_mipi_dsi_driver(visionox_r66451_driver); 346 347 MODULE_AUTHOR("Jessica Zhang <quic_jesszhan@quicinc.com>"); 348 MODULE_DESCRIPTION("Panel driver for the Visionox R66451 AMOLED DSI panel"); 349 MODULE_LICENSE("GPL"); 350