632 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (c) 2018, The Linux Foundation. All rights reserved.4 */5 6#include <linux/backlight.h>7#include <linux/delay.h>8#include <linux/gpio/consumer.h>9#include <linux/module.h>10#include <linux/of.h>11#include <linux/of_graph.h>12#include <linux/pinctrl/consumer.h>13#include <linux/regulator/consumer.h>14 15#include <video/mipi_display.h>16 17#include <drm/drm_mipi_dsi.h>18#include <drm/drm_modes.h>19#include <drm/drm_panel.h>20 21static const char * const regulator_names[] = {22 "vdda",23 "vdispp",24 "vdispn",25};26 27static unsigned long const regulator_enable_loads[] = {28 62000,29 100000,30 100000,31};32 33static unsigned long const regulator_disable_loads[] = {34 80,35 100,36 100,37};38 39struct cmd_set {40 u8 commands[4];41 u8 size;42};43 44struct nt35597_config {45 u32 width_mm;46 u32 height_mm;47 const char *panel_name;48 const struct cmd_set *panel_on_cmds;49 u32 num_on_cmds;50 const struct drm_display_mode *dm;51};52 53struct truly_nt35597 {54 struct device *dev;55 struct drm_panel panel;56 57 struct regulator_bulk_data supplies[ARRAY_SIZE(regulator_names)];58 59 struct gpio_desc *reset_gpio;60 struct gpio_desc *mode_gpio;61 62 struct backlight_device *backlight;63 64 struct mipi_dsi_device *dsi[2];65 66 const struct nt35597_config *config;67};68 69static inline struct truly_nt35597 *panel_to_ctx(struct drm_panel *panel)70{71 return container_of(panel, struct truly_nt35597, panel);72}73 74static const struct cmd_set qcom_2k_panel_magic_cmds[] = {75 /* CMD2_P0 */76 { { 0xff, 0x20 }, 2 },77 { { 0xfb, 0x01 }, 2 },78 { { 0x00, 0x01 }, 2 },79 { { 0x01, 0x55 }, 2 },80 { { 0x02, 0x45 }, 2 },81 { { 0x05, 0x40 }, 2 },82 { { 0x06, 0x19 }, 2 },83 { { 0x07, 0x1e }, 2 },84 { { 0x0b, 0x73 }, 2 },85 { { 0x0c, 0x73 }, 2 },86 { { 0x0e, 0xb0 }, 2 },87 { { 0x0f, 0xae }, 2 },88 { { 0x11, 0xb8 }, 2 },89 { { 0x13, 0x00 }, 2 },90 { { 0x58, 0x80 }, 2 },91 { { 0x59, 0x01 }, 2 },92 { { 0x5a, 0x00 }, 2 },93 { { 0x5b, 0x01 }, 2 },94 { { 0x5c, 0x80 }, 2 },95 { { 0x5d, 0x81 }, 2 },96 { { 0x5e, 0x00 }, 2 },97 { { 0x5f, 0x01 }, 2 },98 { { 0x72, 0x11 }, 2 },99 { { 0x68, 0x03 }, 2 },100 /* CMD2_P4 */101 { { 0xFF, 0x24 }, 2 },102 { { 0xFB, 0x01 }, 2 },103 { { 0x00, 0x1C }, 2 },104 { { 0x01, 0x0B }, 2 },105 { { 0x02, 0x0C }, 2 },106 { { 0x03, 0x01 }, 2 },107 { { 0x04, 0x0F }, 2 },108 { { 0x05, 0x10 }, 2 },109 { { 0x06, 0x10 }, 2 },110 { { 0x07, 0x10 }, 2 },111 { { 0x08, 0x89 }, 2 },112 { { 0x09, 0x8A }, 2 },113 { { 0x0A, 0x13 }, 2 },114 { { 0x0B, 0x13 }, 2 },115 { { 0x0C, 0x15 }, 2 },116 { { 0x0D, 0x15 }, 2 },117 { { 0x0E, 0x17 }, 2 },118 { { 0x0F, 0x17 }, 2 },119 { { 0x10, 0x1C }, 2 },120 { { 0x11, 0x0B }, 2 },121 { { 0x12, 0x0C }, 2 },122 { { 0x13, 0x01 }, 2 },123 { { 0x14, 0x0F }, 2 },124 { { 0x15, 0x10 }, 2 },125 { { 0x16, 0x10 }, 2 },126 { { 0x17, 0x10 }, 2 },127 { { 0x18, 0x89 }, 2 },128 { { 0x19, 0x8A }, 2 },129 { { 0x1A, 0x13 }, 2 },130 { { 0x1B, 0x13 }, 2 },131 { { 0x1C, 0x15 }, 2 },132 { { 0x1D, 0x15 }, 2 },133 { { 0x1E, 0x17 }, 2 },134 { { 0x1F, 0x17 }, 2 },135 /* STV */136 { { 0x20, 0x40 }, 2 },137 { { 0x21, 0x01 }, 2 },138 { { 0x22, 0x00 }, 2 },139 { { 0x23, 0x40 }, 2 },140 { { 0x24, 0x40 }, 2 },141 { { 0x25, 0x6D }, 2 },142 { { 0x26, 0x40 }, 2 },143 { { 0x27, 0x40 }, 2 },144 /* Vend */145 { { 0xE0, 0x00 }, 2 },146 { { 0xDC, 0x21 }, 2 },147 { { 0xDD, 0x22 }, 2 },148 { { 0xDE, 0x07 }, 2 },149 { { 0xDF, 0x07 }, 2 },150 { { 0xE3, 0x6D }, 2 },151 { { 0xE1, 0x07 }, 2 },152 { { 0xE2, 0x07 }, 2 },153 /* UD */154 { { 0x29, 0xD8 }, 2 },155 { { 0x2A, 0x2A }, 2 },156 /* CLK */157 { { 0x4B, 0x03 }, 2 },158 { { 0x4C, 0x11 }, 2 },159 { { 0x4D, 0x10 }, 2 },160 { { 0x4E, 0x01 }, 2 },161 { { 0x4F, 0x01 }, 2 },162 { { 0x50, 0x10 }, 2 },163 { { 0x51, 0x00 }, 2 },164 { { 0x52, 0x80 }, 2 },165 { { 0x53, 0x00 }, 2 },166 { { 0x56, 0x00 }, 2 },167 { { 0x54, 0x07 }, 2 },168 { { 0x58, 0x07 }, 2 },169 { { 0x55, 0x25 }, 2 },170 /* Reset XDONB */171 { { 0x5B, 0x43 }, 2 },172 { { 0x5C, 0x00 }, 2 },173 { { 0x5F, 0x73 }, 2 },174 { { 0x60, 0x73 }, 2 },175 { { 0x63, 0x22 }, 2 },176 { { 0x64, 0x00 }, 2 },177 { { 0x67, 0x08 }, 2 },178 { { 0x68, 0x04 }, 2 },179 /* Resolution:1440x2560 */180 { { 0x72, 0x02 }, 2 },181 /* mux */182 { { 0x7A, 0x80 }, 2 },183 { { 0x7B, 0x91 }, 2 },184 { { 0x7C, 0xD8 }, 2 },185 { { 0x7D, 0x60 }, 2 },186 { { 0x7F, 0x15 }, 2 },187 { { 0x75, 0x15 }, 2 },188 /* ABOFF */189 { { 0xB3, 0xC0 }, 2 },190 { { 0xB4, 0x00 }, 2 },191 { { 0xB5, 0x00 }, 2 },192 /* Source EQ */193 { { 0x78, 0x00 }, 2 },194 { { 0x79, 0x00 }, 2 },195 { { 0x80, 0x00 }, 2 },196 { { 0x83, 0x00 }, 2 },197 /* FP BP */198 { { 0x93, 0x0A }, 2 },199 { { 0x94, 0x0A }, 2 },200 /* Inversion Type */201 { { 0x8A, 0x00 }, 2 },202 { { 0x9B, 0xFF }, 2 },203 /* IMGSWAP =1 @PortSwap=1 */204 { { 0x9D, 0xB0 }, 2 },205 { { 0x9F, 0x63 }, 2 },206 { { 0x98, 0x10 }, 2 },207 /* FRM */208 { { 0xEC, 0x00 }, 2 },209 /* CMD1 */210 { { 0xFF, 0x10 }, 2 },211 /* VBP+VSA=,VFP = 10H */212 { { 0x3B, 0x03, 0x0A, 0x0A }, 4 },213 /* FTE on */214 { { 0x35, 0x00 }, 2 },215 /* EN_BK =1(auto black) */216 { { 0xE5, 0x01 }, 2 },217 /* CMD mode(10) VDO mode(03) */218 { { 0xBB, 0x03 }, 2 },219 /* Non Reload MTP */220 { { 0xFB, 0x01 }, 2 },221};222 223static int truly_dcs_write(struct drm_panel *panel, u32 command)224{225 struct truly_nt35597 *ctx = panel_to_ctx(panel);226 int i, ret;227 228 for (i = 0; i < ARRAY_SIZE(ctx->dsi); i++) {229 ret = mipi_dsi_dcs_write(ctx->dsi[i], command, NULL, 0);230 if (ret < 0) {231 dev_err(ctx->dev, "cmd 0x%x failed for dsi = %d\n", command, i);232 }233 }234 235 return ret;236}237 238static int truly_dcs_write_buf(struct drm_panel *panel,239 u32 size, const u8 *buf)240{241 struct truly_nt35597 *ctx = panel_to_ctx(panel);242 int ret = 0;243 int i;244 245 for (i = 0; i < ARRAY_SIZE(ctx->dsi); i++) {246 ret = mipi_dsi_dcs_write_buffer(ctx->dsi[i], buf, size);247 if (ret < 0) {248 dev_err(ctx->dev, "failed to tx cmd [%d], err: %d\n", i, ret);249 return ret;250 }251 }252 253 return ret;254}255 256static int truly_35597_power_on(struct truly_nt35597 *ctx)257{258 int ret, i;259 260 for (i = 0; i < ARRAY_SIZE(ctx->supplies); i++) {261 ret = regulator_set_load(ctx->supplies[i].consumer,262 regulator_enable_loads[i]);263 if (ret)264 return ret;265 }266 267 ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);268 if (ret < 0)269 return ret;270 271 /*272 * Reset sequence of truly panel requires the panel to be273 * out of reset for 10ms, followed by being held in reset274 * for 10ms and then out again275 */276 gpiod_set_value(ctx->reset_gpio, 0);277 usleep_range(10000, 20000);278 gpiod_set_value(ctx->reset_gpio, 1);279 usleep_range(10000, 20000);280 gpiod_set_value(ctx->reset_gpio, 0);281 usleep_range(10000, 20000);282 283 return 0;284}285 286static int truly_nt35597_power_off(struct truly_nt35597 *ctx)287{288 int ret = 0;289 int i;290 291 gpiod_set_value(ctx->reset_gpio, 1);292 293 for (i = 0; i < ARRAY_SIZE(ctx->supplies); i++) {294 ret = regulator_set_load(ctx->supplies[i].consumer,295 regulator_disable_loads[i]);296 if (ret) {297 dev_err(ctx->dev, "regulator_set_load failed %d\n", ret);298 return ret;299 }300 }301 302 ret = regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);303 if (ret) {304 dev_err(ctx->dev, "regulator_bulk_disable failed %d\n", ret);305 }306 return ret;307}308 309static int truly_nt35597_disable(struct drm_panel *panel)310{311 struct truly_nt35597 *ctx = panel_to_ctx(panel);312 int ret;313 314 if (ctx->backlight) {315 ret = backlight_disable(ctx->backlight);316 if (ret < 0)317 dev_err(ctx->dev, "backlight disable failed %d\n", ret);318 }319 320 return 0;321}322 323static int truly_nt35597_unprepare(struct drm_panel *panel)324{325 struct truly_nt35597 *ctx = panel_to_ctx(panel);326 int ret = 0;327 328 ctx->dsi[0]->mode_flags = 0;329 ctx->dsi[1]->mode_flags = 0;330 331 ret = truly_dcs_write(panel, MIPI_DCS_SET_DISPLAY_OFF);332 if (ret < 0) {333 dev_err(ctx->dev, "set_display_off cmd failed ret = %d\n", ret);334 }335 336 /* 120ms delay required here as per DCS spec */337 msleep(120);338 339 ret = truly_dcs_write(panel, MIPI_DCS_ENTER_SLEEP_MODE);340 if (ret < 0) {341 dev_err(ctx->dev, "enter_sleep cmd failed ret = %d\n", ret);342 }343 344 ret = truly_nt35597_power_off(ctx);345 if (ret < 0)346 dev_err(ctx->dev, "power_off failed ret = %d\n", ret);347 348 return ret;349}350 351static int truly_nt35597_prepare(struct drm_panel *panel)352{353 struct truly_nt35597 *ctx = panel_to_ctx(panel);354 int ret;355 int i;356 const struct cmd_set *panel_on_cmds;357 const struct nt35597_config *config;358 u32 num_cmds;359 360 ret = truly_35597_power_on(ctx);361 if (ret < 0)362 return ret;363 364 ctx->dsi[0]->mode_flags |= MIPI_DSI_MODE_LPM;365 ctx->dsi[1]->mode_flags |= MIPI_DSI_MODE_LPM;366 367 config = ctx->config;368 panel_on_cmds = config->panel_on_cmds;369 num_cmds = config->num_on_cmds;370 371 for (i = 0; i < num_cmds; i++) {372 ret = truly_dcs_write_buf(panel,373 panel_on_cmds[i].size,374 panel_on_cmds[i].commands);375 if (ret < 0) {376 dev_err(ctx->dev, "cmd set tx failed i = %d ret = %d\n", i, ret);377 goto power_off;378 }379 }380 381 ret = truly_dcs_write(panel, MIPI_DCS_EXIT_SLEEP_MODE);382 if (ret < 0) {383 dev_err(ctx->dev, "exit_sleep_mode cmd failed ret = %d\n", ret);384 goto power_off;385 }386 387 /* Per DSI spec wait 120ms after sending exit sleep DCS command */388 msleep(120);389 390 ret = truly_dcs_write(panel, MIPI_DCS_SET_DISPLAY_ON);391 if (ret < 0) {392 dev_err(ctx->dev, "set_display_on cmd failed ret = %d\n", ret);393 goto power_off;394 }395 396 /* Per DSI spec wait 120ms after sending set_display_on DCS command */397 msleep(120);398 399 return 0;400 401power_off:402 if (truly_nt35597_power_off(ctx))403 dev_err(ctx->dev, "power_off failed\n");404 return ret;405}406 407static int truly_nt35597_enable(struct drm_panel *panel)408{409 struct truly_nt35597 *ctx = panel_to_ctx(panel);410 int ret;411 412 if (ctx->backlight) {413 ret = backlight_enable(ctx->backlight);414 if (ret < 0)415 dev_err(ctx->dev, "backlight enable failed %d\n", ret);416 }417 418 return 0;419}420 421static int truly_nt35597_get_modes(struct drm_panel *panel,422 struct drm_connector *connector)423{424 struct truly_nt35597 *ctx = panel_to_ctx(panel);425 struct drm_display_mode *mode;426 const struct nt35597_config *config;427 428 config = ctx->config;429 mode = drm_mode_duplicate(connector->dev, config->dm);430 if (!mode) {431 dev_err(ctx->dev, "failed to create a new display mode\n");432 return 0;433 }434 435 connector->display_info.width_mm = config->width_mm;436 connector->display_info.height_mm = config->height_mm;437 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;438 drm_mode_probed_add(connector, mode);439 440 return 1;441}442 443static const struct drm_panel_funcs truly_nt35597_drm_funcs = {444 .disable = truly_nt35597_disable,445 .unprepare = truly_nt35597_unprepare,446 .prepare = truly_nt35597_prepare,447 .enable = truly_nt35597_enable,448 .get_modes = truly_nt35597_get_modes,449};450 451static int truly_nt35597_panel_add(struct truly_nt35597 *ctx)452{453 struct device *dev = ctx->dev;454 int ret, i;455 456 for (i = 0; i < ARRAY_SIZE(ctx->supplies); i++)457 ctx->supplies[i].supply = regulator_names[i];458 459 ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),460 ctx->supplies);461 if (ret < 0)462 return ret;463 464 ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);465 if (IS_ERR(ctx->reset_gpio)) {466 dev_err(dev, "cannot get reset gpio %ld\n", PTR_ERR(ctx->reset_gpio));467 return PTR_ERR(ctx->reset_gpio);468 }469 470 ctx->mode_gpio = devm_gpiod_get(dev, "mode", GPIOD_OUT_LOW);471 if (IS_ERR(ctx->mode_gpio)) {472 dev_err(dev, "cannot get mode gpio %ld\n", PTR_ERR(ctx->mode_gpio));473 return PTR_ERR(ctx->mode_gpio);474 }475 476 /* dual port */477 gpiod_set_value(ctx->mode_gpio, 0);478 479 drm_panel_init(&ctx->panel, dev, &truly_nt35597_drm_funcs,480 DRM_MODE_CONNECTOR_DSI);481 drm_panel_add(&ctx->panel);482 483 return 0;484}485 486static const struct drm_display_mode qcom_sdm845_mtp_2k_mode = {487 .name = "1440x2560",488 .clock = 268316,489 .hdisplay = 1440,490 .hsync_start = 1440 + 200,491 .hsync_end = 1440 + 200 + 32,492 .htotal = 1440 + 200 + 32 + 64,493 .vdisplay = 2560,494 .vsync_start = 2560 + 8,495 .vsync_end = 2560 + 8 + 1,496 .vtotal = 2560 + 8 + 1 + 7,497 .flags = 0,498};499 500static const struct nt35597_config nt35597_dir = {501 .width_mm = 74,502 .height_mm = 131,503 .panel_name = "qcom_sdm845_mtp_2k_panel",504 .dm = &qcom_sdm845_mtp_2k_mode,505 .panel_on_cmds = qcom_2k_panel_magic_cmds,506 .num_on_cmds = ARRAY_SIZE(qcom_2k_panel_magic_cmds),507};508 509static int truly_nt35597_probe(struct mipi_dsi_device *dsi)510{511 struct device *dev = &dsi->dev;512 struct truly_nt35597 *ctx;513 struct mipi_dsi_device *dsi1_device;514 struct device_node *dsi1;515 struct mipi_dsi_host *dsi1_host;516 struct mipi_dsi_device *dsi_dev;517 int ret = 0;518 int i;519 520 const struct mipi_dsi_device_info info = {521 .type = "trulynt35597",522 .channel = 0,523 .node = NULL,524 };525 526 ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);527 528 if (!ctx)529 return -ENOMEM;530 531 /*532 * This device represents itself as one with two input ports which are533 * fed by the output ports of the two DSI controllers . The DSI0 is534 * the master controller and has most of the panel related info in its535 * child node.536 */537 538 ctx->config = of_device_get_match_data(dev);539 540 if (!ctx->config) {541 dev_err(dev, "missing device configuration\n");542 return -ENODEV;543 }544 545 dsi1 = of_graph_get_remote_node(dsi->dev.of_node, 1, -1);546 if (!dsi1) {547 dev_err(dev, "failed to get remote node for dsi1_device\n");548 return -ENODEV;549 }550 551 dsi1_host = of_find_mipi_dsi_host_by_node(dsi1);552 of_node_put(dsi1);553 if (!dsi1_host)554 return dev_err_probe(dev, -EPROBE_DEFER, "failed to find dsi host\n");555 556 /* register the second DSI device */557 dsi1_device = mipi_dsi_device_register_full(dsi1_host, &info);558 if (IS_ERR(dsi1_device)) {559 dev_err(dev, "failed to create dsi device\n");560 return PTR_ERR(dsi1_device);561 }562 563 mipi_dsi_set_drvdata(dsi, ctx);564 565 ctx->dev = dev;566 ctx->dsi[0] = dsi;567 ctx->dsi[1] = dsi1_device;568 569 ret = truly_nt35597_panel_add(ctx);570 if (ret) {571 dev_err(dev, "failed to add panel\n");572 goto err_panel_add;573 }574 575 for (i = 0; i < ARRAY_SIZE(ctx->dsi); i++) {576 dsi_dev = ctx->dsi[i];577 dsi_dev->lanes = 4;578 dsi_dev->format = MIPI_DSI_FMT_RGB888;579 dsi_dev->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM |580 MIPI_DSI_CLOCK_NON_CONTINUOUS;581 ret = mipi_dsi_attach(dsi_dev);582 if (ret < 0) {583 dev_err(dev, "dsi attach failed i = %d\n", i);584 goto err_dsi_attach;585 }586 }587 588 return 0;589 590err_dsi_attach:591 drm_panel_remove(&ctx->panel);592err_panel_add:593 mipi_dsi_device_unregister(dsi1_device);594 return ret;595}596 597static void truly_nt35597_remove(struct mipi_dsi_device *dsi)598{599 struct truly_nt35597 *ctx = mipi_dsi_get_drvdata(dsi);600 601 if (ctx->dsi[0])602 mipi_dsi_detach(ctx->dsi[0]);603 if (ctx->dsi[1]) {604 mipi_dsi_detach(ctx->dsi[1]);605 mipi_dsi_device_unregister(ctx->dsi[1]);606 }607 608 drm_panel_remove(&ctx->panel);609}610 611static const struct of_device_id truly_nt35597_of_match[] = {612 {613 .compatible = "truly,nt35597-2K-display",614 .data = &nt35597_dir,615 },616 { }617};618MODULE_DEVICE_TABLE(of, truly_nt35597_of_match);619 620static struct mipi_dsi_driver truly_nt35597_driver = {621 .driver = {622 .name = "panel-truly-nt35597",623 .of_match_table = truly_nt35597_of_match,624 },625 .probe = truly_nt35597_probe,626 .remove = truly_nt35597_remove,627};628module_mipi_dsi_driver(truly_nt35597_driver);629 630MODULE_DESCRIPTION("Truly NT35597 DSI Panel Driver");631MODULE_LICENSE("GPL v2");632