552 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2014 Google, Inc.4 *5 * Copyright (C) 2022 Diogo Ivo <diogo.ivo@tecnico.ulisboa.pt>6 *7 * Adapted from the downstream Pixel C driver written by Sean Paul8 */9 10#include <linux/backlight.h>11#include <linux/delay.h>12#include <linux/gpio/consumer.h>13#include <linux/module.h>14#include <linux/of.h>15#include <linux/regulator/consumer.h>16 17#include <video/mipi_display.h>18 19#include <drm/drm_crtc.h>20#include <drm/drm_mipi_dsi.h>21#include <drm/drm_panel.h>22 23#define MCS_CMD_ACS_PROT 0xB024#define MCS_CMD_ACS_PROT_OFF (0 << 0)25 26#define MCS_PWR_CTRL_FUNC 0xD027#define MCS_PWR_CTRL_PARAM1_DEFAULT (2 << 0)28#define MCS_PWR_CTRL_PARAM1_VGH_210_DIV (1 << 4)29#define MCS_PWR_CTRL_PARAM1_VGH_240_DIV (2 << 4)30#define MCS_PWR_CTRL_PARAM1_VGH_280_DIV (3 << 4)31#define MCS_PWR_CTRL_PARAM1_VGH_330_DIV (4 << 4)32#define MCS_PWR_CTRL_PARAM1_VGH_410_DIV (5 << 4)33#define MCS_PWR_CTRL_PARAM2_DEFAULT (9 << 4)34#define MCS_PWR_CTRL_PARAM2_VGL_210_DIV (1 << 0)35#define MCS_PWR_CTRL_PARAM2_VGL_240_DIV (2 << 0)36#define MCS_PWR_CTRL_PARAM2_VGL_280_DIV (3 << 0)37#define MCS_PWR_CTRL_PARAM2_VGL_330_DIV (4 << 0)38#define MCS_PWR_CTRL_PARAM2_VGL_410_DIV (5 << 0)39 40struct jdi_panel {41 struct drm_panel base;42 struct mipi_dsi_device *link1;43 struct mipi_dsi_device *link2;44 45 struct regulator *supply;46 struct regulator *ddi_supply;47 struct backlight_device *backlight;48 49 struct gpio_desc *enable_gpio;50 struct gpio_desc *reset_gpio;51 52 const struct drm_display_mode *mode;53};54 55static inline struct jdi_panel *to_panel_jdi(struct drm_panel *panel)56{57 return container_of(panel, struct jdi_panel, base);58}59 60static void jdi_wait_frames(struct jdi_panel *jdi, unsigned int frames)61{62 unsigned int refresh = drm_mode_vrefresh(jdi->mode);63 64 if (WARN_ON(frames > refresh))65 return;66 67 msleep(1000 / (refresh / frames));68}69 70static int jdi_panel_disable(struct drm_panel *panel)71{72 struct jdi_panel *jdi = to_panel_jdi(panel);73 74 backlight_disable(jdi->backlight);75 76 jdi_wait_frames(jdi, 2);77 78 return 0;79}80 81static int jdi_panel_unprepare(struct drm_panel *panel)82{83 struct jdi_panel *jdi = to_panel_jdi(panel);84 int ret;85 86 ret = mipi_dsi_dcs_set_display_off(jdi->link1);87 if (ret < 0)88 dev_err(panel->dev, "failed to set display off: %d\n", ret);89 90 ret = mipi_dsi_dcs_set_display_off(jdi->link2);91 if (ret < 0)92 dev_err(panel->dev, "failed to set display off: %d\n", ret);93 94 /* Specified by JDI @ 50ms, subject to change */95 msleep(50);96 97 ret = mipi_dsi_dcs_enter_sleep_mode(jdi->link1);98 if (ret < 0)99 dev_err(panel->dev, "failed to enter sleep mode: %d\n", ret);100 ret = mipi_dsi_dcs_enter_sleep_mode(jdi->link2);101 if (ret < 0)102 dev_err(panel->dev, "failed to enter sleep mode: %d\n", ret);103 104 /* Specified by JDI @ 150ms, subject to change */105 msleep(150);106 107 gpiod_set_value(jdi->reset_gpio, 1);108 109 /* T4 = 1ms */110 usleep_range(1000, 3000);111 112 gpiod_set_value(jdi->enable_gpio, 0);113 114 /* T5 = 2ms */115 usleep_range(2000, 4000);116 117 regulator_disable(jdi->ddi_supply);118 119 /* T6 = 2ms plus some time to discharge capacitors */120 usleep_range(7000, 9000);121 122 regulator_disable(jdi->supply);123 /* Specified by JDI @ 20ms, subject to change */124 msleep(20);125 126 return ret;127}128 129static int jdi_setup_symmetrical_split(struct mipi_dsi_device *left,130 struct mipi_dsi_device *right,131 const struct drm_display_mode *mode)132{133 int err;134 135 err = mipi_dsi_dcs_set_column_address(left, 0, mode->hdisplay / 2 - 1);136 if (err < 0) {137 dev_err(&left->dev, "failed to set column address: %d\n", err);138 return err;139 }140 141 err = mipi_dsi_dcs_set_column_address(right, 0, mode->hdisplay / 2 - 1);142 if (err < 0) {143 dev_err(&right->dev, "failed to set column address: %d\n", err);144 return err;145 }146 147 err = mipi_dsi_dcs_set_page_address(left, 0, mode->vdisplay - 1);148 if (err < 0) {149 dev_err(&left->dev, "failed to set page address: %d\n", err);150 return err;151 }152 153 err = mipi_dsi_dcs_set_page_address(right, 0, mode->vdisplay - 1);154 if (err < 0) {155 dev_err(&right->dev, "failed to set page address: %d\n", err);156 return err;157 }158 159 return 0;160}161 162static int jdi_write_dcdc_registers(struct jdi_panel *jdi)163{164 /* Clear the manufacturer command access protection */165 mipi_dsi_generic_write_seq(jdi->link1, MCS_CMD_ACS_PROT,166 MCS_CMD_ACS_PROT_OFF);167 mipi_dsi_generic_write_seq(jdi->link2, MCS_CMD_ACS_PROT,168 MCS_CMD_ACS_PROT_OFF);169 /*170 * Change the VGH/VGL divide rations to move the noise generated by the171 * TCONN. This should hopefully avoid interaction with the backlight172 * controller.173 */174 mipi_dsi_generic_write_seq(jdi->link1, MCS_PWR_CTRL_FUNC,175 MCS_PWR_CTRL_PARAM1_VGH_330_DIV |176 MCS_PWR_CTRL_PARAM1_DEFAULT,177 MCS_PWR_CTRL_PARAM2_VGL_410_DIV |178 MCS_PWR_CTRL_PARAM2_DEFAULT);179 180 mipi_dsi_generic_write_seq(jdi->link2, MCS_PWR_CTRL_FUNC,181 MCS_PWR_CTRL_PARAM1_VGH_330_DIV |182 MCS_PWR_CTRL_PARAM1_DEFAULT,183 MCS_PWR_CTRL_PARAM2_VGL_410_DIV |184 MCS_PWR_CTRL_PARAM2_DEFAULT);185 186 return 0;187}188 189static int jdi_panel_prepare(struct drm_panel *panel)190{191 struct jdi_panel *jdi = to_panel_jdi(panel);192 int err;193 194 /* Disable backlight to avoid showing random pixels195 * with a conservative delay for it to take effect.196 */197 backlight_disable(jdi->backlight);198 jdi_wait_frames(jdi, 3);199 200 jdi->link1->mode_flags |= MIPI_DSI_MODE_LPM;201 jdi->link2->mode_flags |= MIPI_DSI_MODE_LPM;202 203 err = regulator_enable(jdi->supply);204 if (err < 0) {205 dev_err(panel->dev, "failed to enable supply: %d\n", err);206 return err;207 }208 /* T1 = 2ms */209 usleep_range(2000, 4000);210 211 err = regulator_enable(jdi->ddi_supply);212 if (err < 0) {213 dev_err(panel->dev, "failed to enable ddi_supply: %d\n", err);214 goto supply_off;215 }216 /* T2 = 1ms */217 usleep_range(1000, 3000);218 219 gpiod_set_value(jdi->enable_gpio, 1);220 /* T3 = 10ms */221 usleep_range(10000, 15000);222 223 gpiod_set_value(jdi->reset_gpio, 0);224 /* Specified by JDI @ 3ms, subject to change */225 usleep_range(3000, 5000);226 227 /*228 * TODO: The device supports both left-right and even-odd split229 * configurations, but this driver currently supports only the left-230 * right split. To support a different mode a mechanism needs to be231 * put in place to communicate the configuration back to the DSI host232 * controller.233 */234 err = jdi_setup_symmetrical_split(jdi->link1, jdi->link2,235 jdi->mode);236 if (err < 0) {237 dev_err(panel->dev, "failed to set up symmetrical split: %d\n",238 err);239 goto poweroff;240 }241 242 err = mipi_dsi_dcs_set_tear_scanline(jdi->link1,243 jdi->mode->vdisplay - 16);244 if (err < 0) {245 dev_err(panel->dev, "failed to set tear scanline: %d\n", err);246 goto poweroff;247 }248 249 err = mipi_dsi_dcs_set_tear_scanline(jdi->link2,250 jdi->mode->vdisplay - 16);251 if (err < 0) {252 dev_err(panel->dev, "failed to set tear scanline: %d\n", err);253 goto poweroff;254 }255 256 err = mipi_dsi_dcs_set_tear_on(jdi->link1,257 MIPI_DSI_DCS_TEAR_MODE_VBLANK);258 if (err < 0) {259 dev_err(panel->dev, "failed to set tear on: %d\n", err);260 goto poweroff;261 }262 263 err = mipi_dsi_dcs_set_tear_on(jdi->link2,264 MIPI_DSI_DCS_TEAR_MODE_VBLANK);265 if (err < 0) {266 dev_err(panel->dev, "failed to set tear on: %d\n", err);267 goto poweroff;268 }269 270 err = mipi_dsi_dcs_set_pixel_format(jdi->link1, MIPI_DCS_PIXEL_FMT_24BIT);271 if (err < 0) {272 dev_err(panel->dev, "failed to set pixel format: %d\n", err);273 goto poweroff;274 }275 276 err = mipi_dsi_dcs_set_pixel_format(jdi->link2, MIPI_DCS_PIXEL_FMT_24BIT);277 if (err < 0) {278 dev_err(panel->dev, "failed to set pixel format: %d\n", err);279 goto poweroff;280 }281 282 err = mipi_dsi_dcs_exit_sleep_mode(jdi->link1);283 if (err < 0) {284 dev_err(panel->dev, "failed to exit sleep mode: %d\n", err);285 goto poweroff;286 }287 288 err = mipi_dsi_dcs_exit_sleep_mode(jdi->link2);289 if (err < 0) {290 dev_err(panel->dev, "failed to exit sleep mode: %d\n", err);291 goto poweroff;292 }293 294 err = jdi_write_dcdc_registers(jdi);295 if (err < 0) {296 dev_err(panel->dev, "failed to write dcdc registers: %d\n", err);297 goto poweroff;298 }299 /*300 * We need to wait 150ms between mipi_dsi_dcs_exit_sleep_mode() and301 * mipi_dsi_dcs_set_display_on().302 */303 msleep(150);304 305 err = mipi_dsi_dcs_set_display_on(jdi->link1);306 if (err < 0) {307 dev_err(panel->dev, "failed to set display on: %d\n", err);308 goto poweroff;309 }310 311 err = mipi_dsi_dcs_set_display_on(jdi->link2);312 if (err < 0) {313 dev_err(panel->dev, "failed to set display on: %d\n", err);314 goto poweroff;315 }316 317 jdi->link1->mode_flags &= ~MIPI_DSI_MODE_LPM;318 jdi->link2->mode_flags &= ~MIPI_DSI_MODE_LPM;319 320 return 0;321 322poweroff:323 regulator_disable(jdi->ddi_supply);324 325 /* T6 = 2ms plus some time to discharge capacitors */326 usleep_range(7000, 9000);327supply_off:328 regulator_disable(jdi->supply);329 /* Specified by JDI @ 20ms, subject to change */330 msleep(20);331 332 return err;333}334 335static int jdi_panel_enable(struct drm_panel *panel)336{337 struct jdi_panel *jdi = to_panel_jdi(panel);338 339 /*340 * Ensure we send image data before turning the backlight341 * on, to avoid the display showing random pixels.342 */343 jdi_wait_frames(jdi, 3);344 345 backlight_enable(jdi->backlight);346 347 return 0;348}349 350static const struct drm_display_mode default_mode = {351 .clock = (2560 + 80 + 80 + 80) * (1800 + 4 + 4 + 4) * 60 / 1000,352 .hdisplay = 2560,353 .hsync_start = 2560 + 80,354 .hsync_end = 2560 + 80 + 80,355 .htotal = 2560 + 80 + 80 + 80,356 .vdisplay = 1800,357 .vsync_start = 1800 + 4,358 .vsync_end = 1800 + 4 + 4,359 .vtotal = 1800 + 4 + 4 + 4,360 .flags = 0,361};362 363static int jdi_panel_get_modes(struct drm_panel *panel,364 struct drm_connector *connector)365{366 struct drm_display_mode *mode;367 struct jdi_panel *jdi = to_panel_jdi(panel);368 struct device *dev = &jdi->link1->dev;369 370 mode = drm_mode_duplicate(connector->dev, &default_mode);371 if (!mode) {372 dev_err(dev, "failed to add mode %ux%ux@%u\n",373 default_mode.hdisplay, default_mode.vdisplay,374 drm_mode_vrefresh(&default_mode));375 return -ENOMEM;376 }377 378 drm_mode_set_name(mode);379 380 drm_mode_probed_add(connector, mode);381 382 connector->display_info.width_mm = 211;383 connector->display_info.height_mm = 148;384 connector->display_info.bpc = 8;385 386 return 1;387}388 389static const struct drm_panel_funcs jdi_panel_funcs = {390 .prepare = jdi_panel_prepare,391 .enable = jdi_panel_enable,392 .disable = jdi_panel_disable,393 .unprepare = jdi_panel_unprepare,394 .get_modes = jdi_panel_get_modes,395};396 397static const struct of_device_id jdi_of_match[] = {398 { .compatible = "jdi,lpm102a188a", },399 { }400};401MODULE_DEVICE_TABLE(of, jdi_of_match);402 403static int jdi_panel_add(struct jdi_panel *jdi)404{405 struct device *dev = &jdi->link1->dev;406 407 jdi->mode = &default_mode;408 409 jdi->supply = devm_regulator_get(dev, "power");410 if (IS_ERR(jdi->supply))411 return dev_err_probe(dev, PTR_ERR(jdi->supply),412 "failed to get power regulator\n");413 414 jdi->ddi_supply = devm_regulator_get(dev, "ddi");415 if (IS_ERR(jdi->ddi_supply))416 return dev_err_probe(dev, PTR_ERR(jdi->ddi_supply),417 "failed to get ddi regulator\n");418 419 jdi->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);420 if (IS_ERR(jdi->reset_gpio))421 return dev_err_probe(dev, PTR_ERR(jdi->reset_gpio),422 "failed to get reset gpio\n");423 /* T4 = 1ms */424 usleep_range(1000, 3000);425 426 jdi->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);427 if (IS_ERR(jdi->enable_gpio))428 return dev_err_probe(dev, PTR_ERR(jdi->enable_gpio),429 "failed to get enable gpio\n");430 /* T5 = 2ms */431 usleep_range(2000, 4000);432 433 jdi->backlight = devm_of_find_backlight(dev);434 if (IS_ERR(jdi->backlight))435 return dev_err_probe(dev, PTR_ERR(jdi->backlight),436 "failed to create backlight\n");437 438 drm_panel_init(&jdi->base, &jdi->link1->dev, &jdi_panel_funcs,439 DRM_MODE_CONNECTOR_DSI);440 441 drm_panel_add(&jdi->base);442 443 return 0;444}445 446static void jdi_panel_del(struct jdi_panel *jdi)447{448 if (jdi->base.dev)449 drm_panel_remove(&jdi->base);450 451 if (jdi->link2)452 put_device(&jdi->link2->dev);453}454 455static int jdi_panel_dsi_probe(struct mipi_dsi_device *dsi)456{457 struct mipi_dsi_device *secondary = NULL;458 struct jdi_panel *jdi;459 struct device_node *np;460 int err;461 462 dsi->lanes = 4;463 dsi->format = MIPI_DSI_FMT_RGB888;464 dsi->mode_flags = 0;465 466 /* Find DSI-LINK1 */467 np = of_parse_phandle(dsi->dev.of_node, "link2", 0);468 if (np) {469 secondary = of_find_mipi_dsi_device_by_node(np);470 of_node_put(np);471 472 if (!secondary)473 return -EPROBE_DEFER;474 }475 476 /* register a panel for only the DSI-LINK1 interface */477 if (secondary) {478 jdi = devm_kzalloc(&dsi->dev, sizeof(*jdi), GFP_KERNEL);479 if (!jdi) {480 put_device(&secondary->dev);481 return -ENOMEM;482 }483 484 mipi_dsi_set_drvdata(dsi, jdi);485 486 jdi->link1 = dsi;487 jdi->link2 = secondary;488 489 err = jdi_panel_add(jdi);490 if (err < 0) {491 put_device(&secondary->dev);492 return err;493 }494 }495 496 err = mipi_dsi_attach(dsi);497 if (err < 0) {498 if (secondary)499 jdi_panel_del(jdi);500 501 return err;502 }503 504 return 0;505}506 507static void jdi_panel_dsi_remove(struct mipi_dsi_device *dsi)508{509 struct jdi_panel *jdi = mipi_dsi_get_drvdata(dsi);510 int err;511 512 /* only detach from host for the DSI-LINK2 interface */513 if (!jdi)514 mipi_dsi_detach(dsi);515 516 err = jdi_panel_disable(&jdi->base);517 if (err < 0)518 dev_err(&dsi->dev, "failed to disable panel: %d\n", err);519 520 err = mipi_dsi_detach(dsi);521 if (err < 0)522 dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", err);523 524 jdi_panel_del(jdi);525}526 527static void jdi_panel_dsi_shutdown(struct mipi_dsi_device *dsi)528{529 struct jdi_panel *jdi = mipi_dsi_get_drvdata(dsi);530 531 if (!jdi)532 return;533 534 jdi_panel_disable(&jdi->base);535}536 537static struct mipi_dsi_driver jdi_panel_dsi_driver = {538 .driver = {539 .name = "panel-jdi-lpm102a188a",540 .of_match_table = jdi_of_match,541 },542 .probe = jdi_panel_dsi_probe,543 .remove = jdi_panel_dsi_remove,544 .shutdown = jdi_panel_dsi_shutdown,545};546module_mipi_dsi_driver(jdi_panel_dsi_driver);547 548MODULE_AUTHOR("Sean Paul <seanpaul@chromium.org>");549MODULE_AUTHOR("Diogo Ivo <diogo.ivo@tecnico.ulisboa.pt>");550MODULE_DESCRIPTION("DRM Driver for JDI LPM102A188A DSI panel, command mode");551MODULE_LICENSE("GPL");552