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

drm/panel: Add JDI LT070ME05000 WUXGA DSI Panel

Add support for the JDI LT070ME05000 WUXGA DSI panel used in
Nexus 7 2013 devices.

Programming sequence for the panel is was originally found in the
android-msm-flo-3.4-lollipop-release branch from:
https://android.googlesource.com/kernel/msm.git

And video mode setting is from dsi-panel-jdi-dualmipi1-video.dtsi
file in:
git://codeaurora.org/kernel/msm-3.10.git LNX.LA.3.6_rb1.27

Cc: Archit Taneja <archit.taneja@gmail.com>
Cc: Rob Clark <robdclark@gmail.com>
Cc: Sumit Semwal <sumit.semwal@linaro.org>
Cc: John Stultz <john.stultz@linaro.org>
Cc: Emil Velikov <emil.l.velikov@gmail.com>
Cc: Thierry Reding <thierry.reding@gmail.com>
Cc: David Airlie <airlied@linux.ie>
Signed-off-by: Sumit Semwal <sumit.semwal@linaro.org>
Signed-off-by: John Stultz <john.stultz@linaro.org>
Signed-off-by: Vinay Simha BN <simhavcs@gmail.com>
Tested-by: John Stultz <john.stultz@linaro.org>
Reviewed-by: Emil Velikov <emil.l.velikov@gmail.com>
Signed-off-by: Thierry Reding <treding@nvidia.com>

authored by

Vinay Simha BN and committed by
Thierry Reding
c96f5662 cc4b13dd

+544
+11
drivers/gpu/drm/panel/Kconfig
··· 18 18 that it can be automatically turned off when the panel goes into a 19 19 low power state. 20 20 21 + config DRM_PANEL_JDI_LT070ME05000 22 + tristate "JDI LT070ME05000 WUXGA DSI panel" 23 + depends on OF 24 + depends on DRM_MIPI_DSI 25 + depends on BACKLIGHT_CLASS_DEVICE 26 + help 27 + Say Y here if you want to enable support for JDI DSI video mode 28 + panel as found in Google Nexus 7 (2013) devices. 29 + The panel has a 1200(RGB)×1920 (WUXGA) resolution and uses 30 + 24 bit per pixel. 31 + 21 32 config DRM_PANEL_SAMSUNG_LD9040 22 33 tristate "Samsung LD9040 RGB/SPI panel" 23 34 depends on OF && SPI
+1
drivers/gpu/drm/panel/Makefile
··· 1 1 obj-$(CONFIG_DRM_PANEL_SIMPLE) += panel-simple.o 2 + obj-$(CONFIG_DRM_PANEL_JDI_LT070ME05000) += panel-jdi-lt070me05000.o 2 3 obj-$(CONFIG_DRM_PANEL_LG_LG4573) += panel-lg-lg4573.o 3 4 obj-$(CONFIG_DRM_PANEL_PANASONIC_VVX10F034N00) += panel-panasonic-vvx10f034n00.o 4 5 obj-$(CONFIG_DRM_PANEL_SAMSUNG_LD9040) += panel-samsung-ld9040.o
+532
drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
··· 1 + /* 2 + * Copyright (C) 2016 InforceComputing 3 + * Author: Vinay Simha BN <simhavcs@gmail.com> 4 + * 5 + * Copyright (C) 2016 Linaro Ltd 6 + * Author: Sumit Semwal <sumit.semwal@linaro.org> 7 + * 8 + * From internet archives, the panel for Nexus 7 2nd Gen, 2013 model is a 9 + * JDI model LT070ME05000, and its data sheet is at: 10 + * http://panelone.net/en/7-0-inch/JDI_LT070ME05000_7.0_inch-datasheet 11 + * 12 + * This program is free software; you can redistribute it and/or modify it 13 + * under the terms of the GNU General Public License version 2 as published by 14 + * the Free Software Foundation. 15 + * 16 + * This program is distributed in the hope that it will be useful, but WITHOUT 17 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 18 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 19 + * more details. 20 + * 21 + * You should have received a copy of the GNU General Public License along with 22 + * this program. If not, see <http://www.gnu.org/licenses/>. 23 + */ 24 + #include <linux/backlight.h> 25 + #include <linux/gpio/consumer.h> 26 + #include <linux/module.h> 27 + #include <linux/of.h> 28 + #include <linux/regulator/consumer.h> 29 + 30 + #include <drm/drmP.h> 31 + #include <drm/drm_crtc.h> 32 + #include <drm/drm_mipi_dsi.h> 33 + #include <drm/drm_panel.h> 34 + 35 + #include <video/mipi_display.h> 36 + 37 + static const char * const regulator_names[] = { 38 + "vddp", 39 + "iovcc" 40 + }; 41 + 42 + struct jdi_panel { 43 + struct drm_panel base; 44 + struct mipi_dsi_device *dsi; 45 + 46 + struct regulator_bulk_data supplies[ARRAY_SIZE(regulator_names)]; 47 + 48 + struct gpio_desc *enable_gpio; 49 + struct gpio_desc *reset_gpio; 50 + struct gpio_desc *dcdc_en_gpio; 51 + struct backlight_device *backlight; 52 + 53 + bool prepared; 54 + bool enabled; 55 + 56 + const struct drm_display_mode *mode; 57 + }; 58 + 59 + static inline struct jdi_panel *to_jdi_panel(struct drm_panel *panel) 60 + { 61 + return container_of(panel, struct jdi_panel, base); 62 + } 63 + 64 + static int jdi_panel_init(struct jdi_panel *jdi) 65 + { 66 + struct mipi_dsi_device *dsi = jdi->dsi; 67 + struct device *dev = &jdi->dsi->dev; 68 + int ret; 69 + 70 + dsi->mode_flags |= MIPI_DSI_MODE_LPM; 71 + 72 + ret = mipi_dsi_dcs_soft_reset(dsi); 73 + if (ret < 0) 74 + return ret; 75 + 76 + usleep_range(10000, 20000); 77 + 78 + ret = mipi_dsi_dcs_set_pixel_format(dsi, MIPI_DCS_PIXEL_FMT_24BIT << 4); 79 + if (ret < 0) { 80 + dev_err(dev, "failed to set pixel format: %d\n", ret); 81 + return ret; 82 + } 83 + 84 + ret = mipi_dsi_dcs_set_column_address(dsi, 0, jdi->mode->hdisplay - 1); 85 + if (ret < 0) { 86 + dev_err(dev, "failed to set column address: %d\n", ret); 87 + return ret; 88 + } 89 + 90 + ret = mipi_dsi_dcs_set_page_address(dsi, 0, jdi->mode->vdisplay - 1); 91 + if (ret < 0) { 92 + dev_err(dev, "failed to set page address: %d\n", ret); 93 + return ret; 94 + } 95 + 96 + /* 97 + * BIT(5) BCTRL = 1 Backlight Control Block On, Brightness registers 98 + * are active 99 + * BIT(3) BL = 1 Backlight Control On 100 + * BIT(2) DD = 0 Display Dimming is Off 101 + */ 102 + ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY, 103 + (u8[]){ 0x24 }, 1); 104 + if (ret < 0) { 105 + dev_err(dev, "failed to write control display: %d\n", ret); 106 + return ret; 107 + } 108 + 109 + /* CABC off */ 110 + ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_WRITE_POWER_SAVE, 111 + (u8[]){ 0x00 }, 1); 112 + if (ret < 0) { 113 + dev_err(dev, "failed to set cabc off: %d\n", ret); 114 + return ret; 115 + } 116 + 117 + ret = mipi_dsi_dcs_exit_sleep_mode(dsi); 118 + if (ret < 0) { 119 + dev_err(dev, "failed to set exit sleep mode: %d\n", ret); 120 + return ret; 121 + } 122 + 123 + msleep(120); 124 + 125 + ret = mipi_dsi_generic_write(dsi, (u8[]){0xB0, 0x00}, 2); 126 + if (ret < 0) { 127 + dev_err(dev, "failed to set mcap: %d\n", ret); 128 + return ret; 129 + } 130 + 131 + mdelay(10); 132 + 133 + /* Interface setting, video mode */ 134 + ret = mipi_dsi_generic_write(dsi, (u8[]) 135 + {0xB3, 0x26, 0x08, 0x00, 0x20, 0x00}, 6); 136 + if (ret < 0) { 137 + dev_err(dev, "failed to set display interface setting: %d\n" 138 + , ret); 139 + return ret; 140 + } 141 + 142 + mdelay(20); 143 + 144 + ret = mipi_dsi_generic_write(dsi, (u8[]){0xB0, 0x03}, 2); 145 + if (ret < 0) { 146 + dev_err(dev, "failed to set default values for mcap: %d\n" 147 + , ret); 148 + return ret; 149 + } 150 + 151 + return 0; 152 + } 153 + 154 + static int jdi_panel_on(struct jdi_panel *jdi) 155 + { 156 + struct mipi_dsi_device *dsi = jdi->dsi; 157 + struct device *dev = &jdi->dsi->dev; 158 + int ret; 159 + 160 + dsi->mode_flags |= MIPI_DSI_MODE_LPM; 161 + 162 + ret = mipi_dsi_dcs_set_display_on(dsi); 163 + if (ret < 0) 164 + dev_err(dev, "failed to set display on: %d\n", ret); 165 + 166 + return ret; 167 + } 168 + 169 + static void jdi_panel_off(struct jdi_panel *jdi) 170 + { 171 + struct mipi_dsi_device *dsi = jdi->dsi; 172 + struct device *dev = &jdi->dsi->dev; 173 + int ret; 174 + 175 + dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; 176 + 177 + ret = mipi_dsi_dcs_set_display_off(dsi); 178 + if (ret < 0) 179 + dev_err(dev, "failed to set display off: %d\n", ret); 180 + 181 + ret = mipi_dsi_dcs_enter_sleep_mode(dsi); 182 + if (ret < 0) 183 + dev_err(dev, "failed to enter sleep mode: %d\n", ret); 184 + 185 + msleep(100); 186 + } 187 + 188 + static int jdi_panel_disable(struct drm_panel *panel) 189 + { 190 + struct jdi_panel *jdi = to_jdi_panel(panel); 191 + 192 + if (!jdi->enabled) 193 + return 0; 194 + 195 + jdi->backlight->props.power = FB_BLANK_POWERDOWN; 196 + backlight_update_status(jdi->backlight); 197 + 198 + jdi->enabled = false; 199 + 200 + return 0; 201 + } 202 + 203 + static int jdi_panel_unprepare(struct drm_panel *panel) 204 + { 205 + struct jdi_panel *jdi = to_jdi_panel(panel); 206 + struct device *dev = &jdi->dsi->dev; 207 + int ret; 208 + 209 + if (!jdi->prepared) 210 + return 0; 211 + 212 + jdi_panel_off(jdi); 213 + 214 + ret = regulator_bulk_disable(ARRAY_SIZE(jdi->supplies), jdi->supplies); 215 + if (ret < 0) 216 + dev_err(dev, "regulator disable failed, %d\n", ret); 217 + 218 + gpiod_set_value(jdi->enable_gpio, 0); 219 + 220 + gpiod_set_value(jdi->reset_gpio, 1); 221 + 222 + gpiod_set_value(jdi->dcdc_en_gpio, 0); 223 + 224 + jdi->prepared = false; 225 + 226 + return 0; 227 + } 228 + 229 + static int jdi_panel_prepare(struct drm_panel *panel) 230 + { 231 + struct jdi_panel *jdi = to_jdi_panel(panel); 232 + struct device *dev = &jdi->dsi->dev; 233 + int ret; 234 + 235 + if (jdi->prepared) 236 + return 0; 237 + 238 + ret = regulator_bulk_enable(ARRAY_SIZE(jdi->supplies), jdi->supplies); 239 + if (ret < 0) { 240 + dev_err(dev, "regulator enable failed, %d\n", ret); 241 + return ret; 242 + } 243 + 244 + msleep(20); 245 + 246 + gpiod_set_value(jdi->dcdc_en_gpio, 1); 247 + usleep_range(10, 20); 248 + 249 + gpiod_set_value(jdi->reset_gpio, 0); 250 + usleep_range(10, 20); 251 + 252 + gpiod_set_value(jdi->enable_gpio, 1); 253 + usleep_range(10, 20); 254 + 255 + ret = jdi_panel_init(jdi); 256 + if (ret < 0) { 257 + dev_err(dev, "failed to init panel: %d\n", ret); 258 + goto poweroff; 259 + } 260 + 261 + ret = jdi_panel_on(jdi); 262 + if (ret < 0) { 263 + dev_err(dev, "failed to set panel on: %d\n", ret); 264 + goto poweroff; 265 + } 266 + 267 + jdi->prepared = true; 268 + 269 + return 0; 270 + 271 + poweroff: 272 + ret = regulator_bulk_disable(ARRAY_SIZE(jdi->supplies), jdi->supplies); 273 + if (ret < 0) 274 + dev_err(dev, "regulator disable failed, %d\n", ret); 275 + 276 + gpiod_set_value(jdi->enable_gpio, 0); 277 + 278 + gpiod_set_value(jdi->reset_gpio, 1); 279 + 280 + gpiod_set_value(jdi->dcdc_en_gpio, 0); 281 + 282 + return ret; 283 + } 284 + 285 + static int jdi_panel_enable(struct drm_panel *panel) 286 + { 287 + struct jdi_panel *jdi = to_jdi_panel(panel); 288 + 289 + if (jdi->enabled) 290 + return 0; 291 + 292 + jdi->backlight->props.power = FB_BLANK_UNBLANK; 293 + backlight_update_status(jdi->backlight); 294 + 295 + jdi->enabled = true; 296 + 297 + return 0; 298 + } 299 + 300 + static const struct drm_display_mode default_mode = { 301 + .clock = 155493, 302 + .hdisplay = 1200, 303 + .hsync_start = 1200 + 48, 304 + .hsync_end = 1200 + 48 + 32, 305 + .htotal = 1200 + 48 + 32 + 60, 306 + .vdisplay = 1920, 307 + .vsync_start = 1920 + 3, 308 + .vsync_end = 1920 + 3 + 5, 309 + .vtotal = 1920 + 3 + 5 + 6, 310 + .vrefresh = 60, 311 + .flags = 0, 312 + }; 313 + 314 + static int jdi_panel_get_modes(struct drm_panel *panel) 315 + { 316 + struct drm_display_mode *mode; 317 + struct jdi_panel *jdi = to_jdi_panel(panel); 318 + struct device *dev = &jdi->dsi->dev; 319 + 320 + mode = drm_mode_duplicate(panel->drm, &default_mode); 321 + if (!mode) { 322 + dev_err(dev, "failed to add mode %ux%ux@%u\n", 323 + default_mode.hdisplay, default_mode.vdisplay, 324 + default_mode.vrefresh); 325 + return -ENOMEM; 326 + } 327 + 328 + drm_mode_set_name(mode); 329 + 330 + drm_mode_probed_add(panel->connector, mode); 331 + 332 + panel->connector->display_info.width_mm = 95; 333 + panel->connector->display_info.height_mm = 151; 334 + 335 + return 1; 336 + } 337 + 338 + static int dsi_dcs_bl_get_brightness(struct backlight_device *bl) 339 + { 340 + struct mipi_dsi_device *dsi = bl_get_data(bl); 341 + int ret; 342 + u16 brightness = bl->props.brightness; 343 + 344 + dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; 345 + 346 + ret = mipi_dsi_dcs_get_display_brightness(dsi, &brightness); 347 + if (ret < 0) 348 + return ret; 349 + 350 + dsi->mode_flags |= MIPI_DSI_MODE_LPM; 351 + 352 + return brightness & 0xff; 353 + } 354 + 355 + static int dsi_dcs_bl_update_status(struct backlight_device *bl) 356 + { 357 + struct mipi_dsi_device *dsi = bl_get_data(bl); 358 + int ret; 359 + 360 + dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; 361 + 362 + ret = mipi_dsi_dcs_set_display_brightness(dsi, bl->props.brightness); 363 + if (ret < 0) 364 + return ret; 365 + 366 + dsi->mode_flags |= MIPI_DSI_MODE_LPM; 367 + 368 + return 0; 369 + } 370 + 371 + static const struct backlight_ops dsi_bl_ops = { 372 + .update_status = dsi_dcs_bl_update_status, 373 + .get_brightness = dsi_dcs_bl_get_brightness, 374 + }; 375 + 376 + static struct backlight_device * 377 + drm_panel_create_dsi_backlight(struct mipi_dsi_device *dsi) 378 + { 379 + struct device *dev = &dsi->dev; 380 + struct backlight_properties props; 381 + 382 + memset(&props, 0, sizeof(props)); 383 + props.type = BACKLIGHT_RAW; 384 + props.brightness = 255; 385 + props.max_brightness = 255; 386 + 387 + return devm_backlight_device_register(dev, dev_name(dev), dev, dsi, 388 + &dsi_bl_ops, &props); 389 + } 390 + 391 + static const struct drm_panel_funcs jdi_panel_funcs = { 392 + .disable = jdi_panel_disable, 393 + .unprepare = jdi_panel_unprepare, 394 + .prepare = jdi_panel_prepare, 395 + .enable = jdi_panel_enable, 396 + .get_modes = jdi_panel_get_modes, 397 + }; 398 + 399 + static const struct of_device_id jdi_of_match[] = { 400 + { .compatible = "jdi,lt070me05000", }, 401 + { } 402 + }; 403 + MODULE_DEVICE_TABLE(of, jdi_of_match); 404 + 405 + static int jdi_panel_add(struct jdi_panel *jdi) 406 + { 407 + struct device *dev = &jdi->dsi->dev; 408 + int ret; 409 + unsigned int i; 410 + 411 + jdi->mode = &default_mode; 412 + 413 + for (i = 0; i < ARRAY_SIZE(jdi->supplies); i++) 414 + jdi->supplies[i].supply = regulator_names[i]; 415 + 416 + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(jdi->supplies), 417 + jdi->supplies); 418 + if (ret < 0) { 419 + dev_err(dev, "failed to init regulator, ret=%d\n", ret); 420 + return ret; 421 + } 422 + 423 + jdi->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW); 424 + if (IS_ERR(jdi->enable_gpio)) { 425 + ret = PTR_ERR(jdi->enable_gpio); 426 + dev_err(dev, "cannot get enable-gpio %d\n", ret); 427 + return ret; 428 + } 429 + 430 + jdi->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); 431 + if (IS_ERR(jdi->reset_gpio)) { 432 + ret = PTR_ERR(jdi->reset_gpio); 433 + dev_err(dev, "cannot get reset-gpios %d\n", ret); 434 + return ret; 435 + } 436 + 437 + jdi->dcdc_en_gpio = devm_gpiod_get(dev, "dcdc-en", GPIOD_OUT_LOW); 438 + if (IS_ERR(jdi->dcdc_en_gpio)) { 439 + ret = PTR_ERR(jdi->dcdc_en_gpio); 440 + dev_err(dev, "cannot get dcdc-en-gpio %d\n", ret); 441 + return ret; 442 + } 443 + 444 + jdi->backlight = drm_panel_create_dsi_backlight(jdi->dsi); 445 + if (IS_ERR(jdi->backlight)) { 446 + ret = PTR_ERR(jdi->backlight); 447 + dev_err(dev, "failed to register backlight %d\n", ret); 448 + return ret; 449 + } 450 + 451 + drm_panel_init(&jdi->base); 452 + jdi->base.funcs = &jdi_panel_funcs; 453 + jdi->base.dev = &jdi->dsi->dev; 454 + 455 + ret = drm_panel_add(&jdi->base); 456 + 457 + return ret; 458 + } 459 + 460 + static void jdi_panel_del(struct jdi_panel *jdi) 461 + { 462 + if (jdi->base.dev) 463 + drm_panel_remove(&jdi->base); 464 + } 465 + 466 + static int jdi_panel_probe(struct mipi_dsi_device *dsi) 467 + { 468 + struct jdi_panel *jdi; 469 + int ret; 470 + 471 + dsi->lanes = 4; 472 + dsi->format = MIPI_DSI_FMT_RGB888; 473 + dsi->mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | 474 + MIPI_DSI_CLOCK_NON_CONTINUOUS; 475 + 476 + jdi = devm_kzalloc(&dsi->dev, sizeof(*jdi), GFP_KERNEL); 477 + if (!jdi) 478 + return -ENOMEM; 479 + 480 + mipi_dsi_set_drvdata(dsi, jdi); 481 + 482 + jdi->dsi = dsi; 483 + 484 + ret = jdi_panel_add(jdi); 485 + if (ret < 0) 486 + return ret; 487 + 488 + return mipi_dsi_attach(dsi); 489 + } 490 + 491 + static int jdi_panel_remove(struct mipi_dsi_device *dsi) 492 + { 493 + struct jdi_panel *jdi = mipi_dsi_get_drvdata(dsi); 494 + int ret; 495 + 496 + ret = jdi_panel_disable(&jdi->base); 497 + if (ret < 0) 498 + dev_err(&dsi->dev, "failed to disable panel: %d\n", ret); 499 + 500 + ret = mipi_dsi_detach(dsi); 501 + if (ret < 0) 502 + dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", 503 + ret); 504 + 505 + drm_panel_detach(&jdi->base); 506 + jdi_panel_del(jdi); 507 + 508 + return 0; 509 + } 510 + 511 + static void jdi_panel_shutdown(struct mipi_dsi_device *dsi) 512 + { 513 + struct jdi_panel *jdi = mipi_dsi_get_drvdata(dsi); 514 + 515 + jdi_panel_disable(&jdi->base); 516 + } 517 + 518 + static struct mipi_dsi_driver jdi_panel_driver = { 519 + .driver = { 520 + .name = "panel-jdi-lt070me05000", 521 + .of_match_table = jdi_of_match, 522 + }, 523 + .probe = jdi_panel_probe, 524 + .remove = jdi_panel_remove, 525 + .shutdown = jdi_panel_shutdown, 526 + }; 527 + module_mipi_dsi_driver(jdi_panel_driver); 528 + 529 + MODULE_AUTHOR("Sumit Semwal <sumit.semwal@linaro.org>"); 530 + MODULE_AUTHOR("Vinay Simha BN <simhavcs@gmail.com>"); 531 + MODULE_DESCRIPTION("JDI LT070ME05000 WUXGA"); 532 + MODULE_LICENSE("GPL v2");