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_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); 259 if (!ctx) 260 return -ENOMEM; 261 262 dsc = devm_kzalloc(dev, sizeof(*dsc), GFP_KERNEL); 263 if (!dsc) 264 return -ENOMEM; 265 266 /* Set DSC params */ 267 dsc->dsc_version_major = 0x1; 268 dsc->dsc_version_minor = 0x2; 269 270 dsc->slice_height = 20; 271 dsc->slice_width = 540; 272 dsc->slice_count = 2; 273 dsc->bits_per_component = 8; 274 dsc->bits_per_pixel = 8 << 4; 275 dsc->block_pred_enable = true; 276 277 dsi->dsc = dsc; 278 279 ctx->supplies[0].supply = "vddio"; 280 ctx->supplies[1].supply = "vdd"; 281 282 ret = devm_regulator_bulk_get(&dsi->dev, ARRAY_SIZE(ctx->supplies), 283 ctx->supplies); 284 285 if (ret < 0) 286 return ret; 287 288 ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); 289 if (IS_ERR(ctx->reset_gpio)) 290 return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), "Failed to get reset-gpios\n"); 291 292 ctx->dsi = dsi; 293 mipi_dsi_set_drvdata(dsi, ctx); 294 295 dsi->lanes = 4; 296 dsi->format = MIPI_DSI_FMT_RGB888; 297 dsi->mode_flags = MIPI_DSI_MODE_LPM | MIPI_DSI_CLOCK_NON_CONTINUOUS; 298 ctx->panel.prepare_prev_first = true; 299 300 drm_panel_init(&ctx->panel, dev, &visionox_r66451_funcs, DRM_MODE_CONNECTOR_DSI); 301 ctx->panel.backlight = visionox_r66451_create_backlight(dsi); 302 if (IS_ERR(ctx->panel.backlight)) 303 return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight), 304 "Failed to create backlight\n"); 305 306 drm_panel_add(&ctx->panel); 307 308 ret = mipi_dsi_attach(dsi); 309 if (ret < 0) { 310 dev_err(dev, "Failed to attach to DSI host: %d\n", ret); 311 drm_panel_remove(&ctx->panel); 312 } 313 314 return ret; 315 } 316 317 static void visionox_r66451_remove(struct mipi_dsi_device *dsi) 318 { 319 struct visionox_r66451 *ctx = mipi_dsi_get_drvdata(dsi); 320 int ret; 321 322 ret = mipi_dsi_detach(dsi); 323 if (ret < 0) 324 dev_err(&dsi->dev, "Failed to detach DSI host: %d\n", ret); 325 326 drm_panel_remove(&ctx->panel); 327 } 328 329 static const struct of_device_id visionox_r66451_of_match[] = { 330 {.compatible = "visionox,r66451"}, 331 { /*sentinel*/ } 332 }; 333 MODULE_DEVICE_TABLE(of, visionox_r66451_of_match); 334 335 static struct mipi_dsi_driver visionox_r66451_driver = { 336 .probe = visionox_r66451_probe, 337 .remove = visionox_r66451_remove, 338 .driver = { 339 .name = "panel-visionox-r66451", 340 .of_match_table = visionox_r66451_of_match, 341 }, 342 }; 343 344 module_mipi_dsi_driver(visionox_r66451_driver); 345 346 MODULE_AUTHOR("Jessica Zhang <quic_jesszhan@quicinc.com>"); 347 MODULE_DESCRIPTION("Panel driver for the Visionox R66451 AMOLED DSI panel"); 348 MODULE_LICENSE("GPL"); 349