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

Configure Feed

Select the types of activity you want to include in your feed.

drm/panel: Add driver for BOE RM692E5 AMOLED panel

Add support for the 2700x1224 AMOLED BOE panel bundled with a RM692E5
driver IC, as found on the Fairphone 5 smartphone.

Co-developed-by: Luca Weiss <luca.weiss@fairphone.com>
Signed-off-by: Luca Weiss <luca.weiss@fairphone.com>
Signed-off-by: Konrad Dybcio <konrad.dybcio@linaro.org>
Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>
Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
Link: https://patchwork.freedesktop.org/patch/msgid/20230927-topic-fp5_disp-v2-2-7b5e1d1662a6@linaro.org

authored by

Konrad Dybcio and committed by
Neil Armstrong
988d0ff2 d1fd19e6

+433
+9
drivers/gpu/drm/panel/Kconfig
··· 516 516 Say Y here if you want to enable support for Raydium RM68200 517 517 720x1280 DSI video mode panel. 518 518 519 + config DRM_PANEL_RAYDIUM_RM692E5 520 + tristate "Raydium RM692E5-based DSI panel" 521 + depends on OF 522 + depends on DRM_MIPI_DSI 523 + depends on BACKLIGHT_CLASS_DEVICE 524 + help 525 + Say Y here if you want to enable support for Raydium RM692E5-based 526 + display panels, such as the one found in the Fairphone 5 smartphone. 527 + 519 528 config DRM_PANEL_RONBO_RB070D30 520 529 tristate "Ronbo Electronics RB070D30 panel" 521 530 depends on OF
+1
drivers/gpu/drm/panel/Makefile
··· 49 49 obj-$(CONFIG_DRM_PANEL_RASPBERRYPI_TOUCHSCREEN) += panel-raspberrypi-touchscreen.o 50 50 obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM67191) += panel-raydium-rm67191.o 51 51 obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM68200) += panel-raydium-rm68200.o 52 + obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM692E5) += panel-raydium-rm692e5.o 52 53 obj-$(CONFIG_DRM_PANEL_RONBO_RB070D30) += panel-ronbo-rb070d30.o 53 54 obj-$(CONFIG_DRM_PANEL_SAMSUNG_ATNA33XC20) += panel-samsung-atna33xc20.o 54 55 obj-$(CONFIG_DRM_PANEL_SAMSUNG_DB7430) += panel-samsung-db7430.o
+423
drivers/gpu/drm/panel/panel-raydium-rm692e5.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) 2023 Linaro Limited 5 + */ 6 + 7 + #include <linux/backlight.h> 8 + #include <linux/delay.h> 9 + #include <linux/gpio/consumer.h> 10 + #include <linux/module.h> 11 + #include <linux/of.h> 12 + #include <linux/regulator/consumer.h> 13 + 14 + #include <drm/display/drm_dsc.h> 15 + #include <drm/display/drm_dsc_helper.h> 16 + #include <drm/drm_mipi_dsi.h> 17 + #include <drm/drm_modes.h> 18 + #include <drm/drm_panel.h> 19 + 20 + struct rm692e5_panel { 21 + struct drm_panel panel; 22 + struct mipi_dsi_device *dsi; 23 + struct drm_dsc_config dsc; 24 + struct regulator_bulk_data supplies[3]; 25 + struct gpio_desc *reset_gpio; 26 + bool prepared; 27 + }; 28 + 29 + static inline struct rm692e5_panel *to_rm692e5_panel(struct drm_panel *panel) 30 + { 31 + return container_of(panel, struct rm692e5_panel, panel); 32 + } 33 + 34 + static void rm692e5_reset(struct rm692e5_panel *ctx) 35 + { 36 + gpiod_set_value_cansleep(ctx->reset_gpio, 0); 37 + usleep_range(10000, 11000); 38 + gpiod_set_value_cansleep(ctx->reset_gpio, 1); 39 + usleep_range(5000, 6000); 40 + gpiod_set_value_cansleep(ctx->reset_gpio, 0); 41 + usleep_range(10000, 11000); 42 + } 43 + 44 + static int rm692e5_on(struct rm692e5_panel *ctx) 45 + { 46 + struct mipi_dsi_device *dsi = ctx->dsi; 47 + struct device *dev = &dsi->dev; 48 + int ret; 49 + 50 + dsi->mode_flags |= MIPI_DSI_MODE_LPM; 51 + 52 + mipi_dsi_generic_write_seq(dsi, 0xfe, 0x41); 53 + mipi_dsi_generic_write_seq(dsi, 0xd6, 0x00); 54 + mipi_dsi_generic_write_seq(dsi, 0xfe, 0x16); 55 + mipi_dsi_generic_write_seq(dsi, 0x8a, 0x87); 56 + mipi_dsi_generic_write_seq(dsi, 0xfe, 0x71); 57 + mipi_dsi_generic_write_seq(dsi, 0x82, 0x01); 58 + mipi_dsi_generic_write_seq(dsi, 0xc6, 0x00); 59 + mipi_dsi_generic_write_seq(dsi, 0xc7, 0x2c); 60 + mipi_dsi_generic_write_seq(dsi, 0xc8, 0x64); 61 + mipi_dsi_generic_write_seq(dsi, 0xc9, 0x3c); 62 + mipi_dsi_generic_write_seq(dsi, 0xca, 0x80); 63 + mipi_dsi_generic_write_seq(dsi, 0xcb, 0x02); 64 + mipi_dsi_generic_write_seq(dsi, 0xcc, 0x02); 65 + mipi_dsi_generic_write_seq(dsi, 0xfe, 0x38); 66 + mipi_dsi_generic_write_seq(dsi, 0x18, 0x13); 67 + mipi_dsi_generic_write_seq(dsi, 0xfe, 0xf4); 68 + mipi_dsi_generic_write_seq(dsi, 0x00, 0xff); 69 + mipi_dsi_generic_write_seq(dsi, 0x01, 0xff); 70 + mipi_dsi_generic_write_seq(dsi, 0x02, 0xcf); 71 + mipi_dsi_generic_write_seq(dsi, 0x03, 0xbc); 72 + mipi_dsi_generic_write_seq(dsi, 0x04, 0xb9); 73 + mipi_dsi_generic_write_seq(dsi, 0x05, 0x99); 74 + mipi_dsi_generic_write_seq(dsi, 0x06, 0x02); 75 + mipi_dsi_generic_write_seq(dsi, 0x07, 0x0a); 76 + mipi_dsi_generic_write_seq(dsi, 0x08, 0xe0); 77 + mipi_dsi_generic_write_seq(dsi, 0x09, 0x4c); 78 + mipi_dsi_generic_write_seq(dsi, 0x0a, 0xeb); 79 + mipi_dsi_generic_write_seq(dsi, 0x0b, 0xe8); 80 + mipi_dsi_generic_write_seq(dsi, 0x0c, 0x32); 81 + mipi_dsi_generic_write_seq(dsi, 0x0d, 0x07); 82 + mipi_dsi_generic_write_seq(dsi, 0xfe, 0xf4); 83 + mipi_dsi_generic_write_seq(dsi, 0x0d, 0xc0); 84 + mipi_dsi_generic_write_seq(dsi, 0x0e, 0xff); 85 + mipi_dsi_generic_write_seq(dsi, 0x0f, 0xff); 86 + mipi_dsi_generic_write_seq(dsi, 0x10, 0x33); 87 + mipi_dsi_generic_write_seq(dsi, 0x11, 0x6f); 88 + mipi_dsi_generic_write_seq(dsi, 0x12, 0x6e); 89 + mipi_dsi_generic_write_seq(dsi, 0x13, 0xa6); 90 + mipi_dsi_generic_write_seq(dsi, 0x14, 0x80); 91 + mipi_dsi_generic_write_seq(dsi, 0x15, 0x02); 92 + mipi_dsi_generic_write_seq(dsi, 0x16, 0x38); 93 + mipi_dsi_generic_write_seq(dsi, 0x17, 0xd3); 94 + mipi_dsi_generic_write_seq(dsi, 0x18, 0x3a); 95 + mipi_dsi_generic_write_seq(dsi, 0x19, 0xba); 96 + mipi_dsi_generic_write_seq(dsi, 0x1a, 0xcc); 97 + mipi_dsi_generic_write_seq(dsi, 0x1b, 0x01); 98 + 99 + ret = mipi_dsi_dcs_nop(dsi); 100 + if (ret < 0) { 101 + dev_err(dev, "Failed to nop: %d\n", ret); 102 + return ret; 103 + } 104 + msleep(32); 105 + 106 + mipi_dsi_generic_write_seq(dsi, 0xfe, 0x38); 107 + mipi_dsi_generic_write_seq(dsi, 0x18, 0x13); 108 + mipi_dsi_generic_write_seq(dsi, 0xfe, 0xd1); 109 + mipi_dsi_generic_write_seq(dsi, 0xd3, 0x00); 110 + mipi_dsi_generic_write_seq(dsi, 0xd0, 0x00); 111 + mipi_dsi_generic_write_seq(dsi, 0xd2, 0x00); 112 + mipi_dsi_generic_write_seq(dsi, 0xd4, 0x00); 113 + mipi_dsi_generic_write_seq(dsi, 0xb4, 0x01); 114 + mipi_dsi_generic_write_seq(dsi, 0xfe, 0xf9); 115 + mipi_dsi_generic_write_seq(dsi, 0x00, 0xaf); 116 + mipi_dsi_generic_write_seq(dsi, 0x1d, 0x37); 117 + mipi_dsi_generic_write_seq(dsi, 0x44, 0x0a, 0x7b); 118 + mipi_dsi_generic_write_seq(dsi, 0xfe, 0x00); 119 + mipi_dsi_generic_write_seq(dsi, 0xfa, 0x01); 120 + mipi_dsi_generic_write_seq(dsi, 0xc2, 0x08); 121 + mipi_dsi_generic_write_seq(dsi, 0x35, 0x00); 122 + mipi_dsi_generic_write_seq(dsi, 0x51, 0x05, 0x42); 123 + 124 + ret = mipi_dsi_dcs_exit_sleep_mode(dsi); 125 + if (ret < 0) { 126 + dev_err(dev, "Failed to exit sleep mode: %d\n", ret); 127 + return ret; 128 + } 129 + msleep(100); 130 + 131 + ret = mipi_dsi_dcs_set_display_on(dsi); 132 + if (ret < 0) { 133 + dev_err(dev, "Failed to set display on: %d\n", ret); 134 + return ret; 135 + } 136 + 137 + return 0; 138 + } 139 + 140 + static int rm692e5_disable(struct drm_panel *panel) 141 + { 142 + struct rm692e5_panel *ctx = to_rm692e5_panel(panel); 143 + struct mipi_dsi_device *dsi = ctx->dsi; 144 + struct device *dev = &dsi->dev; 145 + int ret; 146 + 147 + dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; 148 + 149 + mipi_dsi_generic_write_seq(dsi, 0xfe, 0x00); 150 + 151 + ret = mipi_dsi_dcs_set_display_off(dsi); 152 + if (ret < 0) { 153 + dev_err(dev, "Failed to set display off: %d\n", ret); 154 + return ret; 155 + } 156 + 157 + ret = mipi_dsi_dcs_enter_sleep_mode(dsi); 158 + if (ret < 0) { 159 + dev_err(dev, "Failed to enter sleep mode: %d\n", ret); 160 + return ret; 161 + } 162 + msleep(100); 163 + 164 + return 0; 165 + } 166 + 167 + static int rm692e5_prepare(struct drm_panel *panel) 168 + { 169 + struct rm692e5_panel *ctx = to_rm692e5_panel(panel); 170 + struct drm_dsc_picture_parameter_set pps; 171 + struct device *dev = &ctx->dsi->dev; 172 + int ret; 173 + 174 + if (ctx->prepared) 175 + return 0; 176 + 177 + ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies); 178 + if (ret < 0) { 179 + dev_err(dev, "Failed to enable regulators: %d\n", ret); 180 + return ret; 181 + } 182 + 183 + rm692e5_reset(ctx); 184 + 185 + ret = rm692e5_on(ctx); 186 + if (ret < 0) { 187 + dev_err(dev, "Failed to initialize panel: %d\n", ret); 188 + gpiod_set_value_cansleep(ctx->reset_gpio, 1); 189 + regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); 190 + return ret; 191 + } 192 + 193 + drm_dsc_pps_payload_pack(&pps, &ctx->dsc); 194 + 195 + ret = mipi_dsi_picture_parameter_set(ctx->dsi, &pps); 196 + if (ret < 0) { 197 + dev_err(panel->dev, "failed to transmit PPS: %d\n", ret); 198 + return ret; 199 + } 200 + 201 + ret = mipi_dsi_compression_mode(ctx->dsi, true); 202 + if (ret < 0) { 203 + dev_err(dev, "failed to enable compression mode: %d\n", ret); 204 + return ret; 205 + } 206 + 207 + msleep(28); 208 + 209 + mipi_dsi_generic_write_seq(ctx->dsi, 0xfe, 0x40); 210 + 211 + /* 0x05 -> 90Hz, 0x00 -> 60Hz */ 212 + mipi_dsi_generic_write_seq(ctx->dsi, 0xbd, 0x05); 213 + 214 + mipi_dsi_generic_write_seq(ctx->dsi, 0xfe, 0x00); 215 + 216 + ctx->prepared = true; 217 + 218 + return 0; 219 + } 220 + 221 + static int rm692e5_unprepare(struct drm_panel *panel) 222 + { 223 + struct rm692e5_panel *ctx = to_rm692e5_panel(panel); 224 + 225 + if (!ctx->prepared) 226 + return 0; 227 + 228 + gpiod_set_value_cansleep(ctx->reset_gpio, 1); 229 + regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); 230 + 231 + ctx->prepared = false; 232 + return 0; 233 + } 234 + 235 + static const struct drm_display_mode rm692e5_mode = { 236 + .clock = (1224 + 32 + 8 + 8) * (2700 + 8 + 2 + 8) * 90 / 1000, 237 + .hdisplay = 1224, 238 + .hsync_start = 1224 + 32, 239 + .hsync_end = 1224 + 32 + 8, 240 + .htotal = 1224 + 32 + 8 + 8, 241 + .vdisplay = 2700, 242 + .vsync_start = 2700 + 8, 243 + .vsync_end = 2700 + 8 + 2, 244 + .vtotal = 2700 + 8 + 2 + 8, 245 + .width_mm = 68, 246 + .height_mm = 150, 247 + }; 248 + 249 + static int rm692e5_get_modes(struct drm_panel *panel, 250 + struct drm_connector *connector) 251 + { 252 + struct drm_display_mode *mode; 253 + 254 + mode = drm_mode_duplicate(connector->dev, &rm692e5_mode); 255 + if (!mode) 256 + return -ENOMEM; 257 + 258 + drm_mode_set_name(mode); 259 + 260 + mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; 261 + connector->display_info.width_mm = mode->width_mm; 262 + connector->display_info.height_mm = mode->height_mm; 263 + drm_mode_probed_add(connector, mode); 264 + 265 + return 1; 266 + } 267 + 268 + static const struct drm_panel_funcs rm692e5_panel_funcs = { 269 + .prepare = rm692e5_prepare, 270 + .unprepare = rm692e5_unprepare, 271 + .disable = rm692e5_disable, 272 + .get_modes = rm692e5_get_modes, 273 + }; 274 + 275 + static int rm692e5_bl_update_status(struct backlight_device *bl) 276 + { 277 + struct mipi_dsi_device *dsi = bl_get_data(bl); 278 + u16 brightness = backlight_get_brightness(bl); 279 + int ret; 280 + 281 + dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; 282 + 283 + ret = mipi_dsi_dcs_set_display_brightness_large(dsi, brightness); 284 + if (ret < 0) 285 + return ret; 286 + 287 + dsi->mode_flags |= MIPI_DSI_MODE_LPM; 288 + 289 + return 0; 290 + } 291 + 292 + static int rm692e5_bl_get_brightness(struct backlight_device *bl) 293 + { 294 + struct mipi_dsi_device *dsi = bl_get_data(bl); 295 + u16 brightness; 296 + int ret; 297 + 298 + dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; 299 + 300 + ret = mipi_dsi_dcs_get_display_brightness_large(dsi, &brightness); 301 + if (ret < 0) 302 + return ret; 303 + 304 + dsi->mode_flags |= MIPI_DSI_MODE_LPM; 305 + 306 + return brightness; 307 + } 308 + 309 + static const struct backlight_ops rm692e5_bl_ops = { 310 + .update_status = rm692e5_bl_update_status, 311 + .get_brightness = rm692e5_bl_get_brightness, 312 + }; 313 + 314 + static struct backlight_device * 315 + rm692e5_create_backlight(struct mipi_dsi_device *dsi) 316 + { 317 + struct device *dev = &dsi->dev; 318 + const struct backlight_properties props = { 319 + .type = BACKLIGHT_RAW, 320 + .brightness = 4095, 321 + .max_brightness = 4095, 322 + }; 323 + 324 + return devm_backlight_device_register(dev, dev_name(dev), dev, dsi, 325 + &rm692e5_bl_ops, &props); 326 + } 327 + 328 + static int rm692e5_probe(struct mipi_dsi_device *dsi) 329 + { 330 + struct device *dev = &dsi->dev; 331 + struct rm692e5_panel *ctx; 332 + int ret; 333 + 334 + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); 335 + if (!ctx) 336 + return -ENOMEM; 337 + 338 + ctx->supplies[0].supply = "vddio"; 339 + ctx->supplies[1].supply = "dvdd"; 340 + ctx->supplies[2].supply = "vci"; 341 + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies), 342 + ctx->supplies); 343 + if (ret < 0) 344 + return dev_err_probe(dev, ret, "Failed to get regulators\n"); 345 + 346 + ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); 347 + if (IS_ERR(ctx->reset_gpio)) 348 + return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), 349 + "Failed to get reset-gpios\n"); 350 + 351 + ctx->dsi = dsi; 352 + mipi_dsi_set_drvdata(dsi, ctx); 353 + 354 + dsi->lanes = 4; 355 + dsi->format = MIPI_DSI_FMT_RGB888; 356 + dsi->mode_flags = MIPI_DSI_MODE_NO_EOT_PACKET | 357 + MIPI_DSI_CLOCK_NON_CONTINUOUS; 358 + 359 + drm_panel_init(&ctx->panel, dev, &rm692e5_panel_funcs, 360 + DRM_MODE_CONNECTOR_DSI); 361 + ctx->panel.prepare_prev_first = true; 362 + 363 + ctx->panel.backlight = rm692e5_create_backlight(dsi); 364 + if (IS_ERR(ctx->panel.backlight)) 365 + return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight), 366 + "Failed to create backlight\n"); 367 + 368 + drm_panel_add(&ctx->panel); 369 + 370 + /* This panel only supports DSC; unconditionally enable it */ 371 + dsi->dsc = &ctx->dsc; 372 + 373 + /* TODO: Pass slice_per_pkt = 2 */ 374 + ctx->dsc.dsc_version_major = 1; 375 + ctx->dsc.dsc_version_minor = 1; 376 + ctx->dsc.slice_height = 60; 377 + ctx->dsc.slice_width = 1224; 378 + 379 + ctx->dsc.slice_count = 1224 / ctx->dsc.slice_width; 380 + ctx->dsc.bits_per_component = 8; 381 + ctx->dsc.bits_per_pixel = 8 << 4; /* 4 fractional bits */ 382 + ctx->dsc.block_pred_enable = true; 383 + 384 + ret = mipi_dsi_attach(dsi); 385 + if (ret < 0) { 386 + dev_err(dev, "Failed to attach to DSI host: %d\n", ret); 387 + drm_panel_remove(&ctx->panel); 388 + return ret; 389 + } 390 + 391 + return 0; 392 + } 393 + 394 + static void rm692e5_remove(struct mipi_dsi_device *dsi) 395 + { 396 + struct rm692e5_panel *ctx = mipi_dsi_get_drvdata(dsi); 397 + int ret; 398 + 399 + ret = mipi_dsi_detach(dsi); 400 + if (ret < 0) 401 + dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret); 402 + 403 + drm_panel_remove(&ctx->panel); 404 + } 405 + 406 + static const struct of_device_id rm692e5_of_match[] = { 407 + { .compatible = "fairphone,fp5-rm692e5-boe" }, 408 + { } 409 + }; 410 + MODULE_DEVICE_TABLE(of, rm692e5_of_match); 411 + 412 + static struct mipi_dsi_driver rm692e5_driver = { 413 + .probe = rm692e5_probe, 414 + .remove = rm692e5_remove, 415 + .driver = { 416 + .name = "panel-rm692e5-boe-amoled", 417 + .of_match_table = rm692e5_of_match, 418 + }, 419 + }; 420 + module_mipi_dsi_driver(rm692e5_driver); 421 + 422 + MODULE_DESCRIPTION("DRM driver for rm692e5-equipped DSI panels"); 423 + MODULE_LICENSE("GPL");