611 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Novatek NT35950 DriverIC panels driver4 *5 * Copyright (c) 2021 AngeloGioacchino Del Regno6 * <angelogioacchino.delregno@somainline.org>7 */8#include <linux/delay.h>9#include <linux/gpio/consumer.h>10#include <linux/module.h>11#include <linux/of.h>12#include <linux/of_graph.h>13#include <linux/regulator/consumer.h>14 15#include <drm/drm_connector.h>16#include <drm/drm_crtc.h>17#include <drm/drm_mipi_dsi.h>18#include <drm/drm_modes.h>19#include <drm/drm_panel.h>20 21#define MCS_CMD_MAUCCTR 0xf0 /* Manufacturer command enable */22#define MCS_PARAM_SCALER_FUNCTION 0x58 /* Scale-up function */23#define MCS_PARAM_SCALEUP_MODE 0xc924 #define MCS_SCALEUP_SIMPLE 0x025 #define MCS_SCALEUP_BILINEAR BIT(0)26 #define MCS_SCALEUP_DUPLICATE (BIT(0) | BIT(4))27 28/* VESA Display Stream Compression param */29#define MCS_PARAM_VESA_DSC_ON 0x0330 31/* Data Compression mode */32#define MCS_PARAM_DATA_COMPRESSION 0x9033 #define MCS_DATA_COMPRESSION_NONE 0x0034 #define MCS_DATA_COMPRESSION_FBC 0x0235 #define MCS_DATA_COMPRESSION_DSC 0x0336 37/* Display Output control */38#define MCS_PARAM_DISP_OUTPUT_CTRL 0xb439 #define MCS_DISP_OUT_SRAM_EN BIT(0)40 #define MCS_DISP_OUT_VIDEO_MODE BIT(4)41 42/* VESA Display Stream Compression setting */43#define MCS_PARAM_VESA_DSC_SETTING 0xc044 45/* SubPixel Rendering (SPR) */46#define MCS_PARAM_SPR_EN 0xe347#define MCS_PARAM_SPR_MODE 0xef48 #define MCS_SPR_MODE_YYG_RAINBOW_RGB 0x0149 50#define NT35950_VREG_MAX 451 52struct nt35950 {53 struct drm_panel panel;54 struct drm_connector *connector;55 struct mipi_dsi_device *dsi[2];56 struct regulator_bulk_data vregs[NT35950_VREG_MAX];57 struct gpio_desc *reset_gpio;58 const struct nt35950_panel_desc *desc;59 60 int cur_mode;61 u8 last_page;62};63 64struct nt35950_panel_mode {65 const struct drm_display_mode mode;66 67 bool enable_sram;68 bool is_video_mode;69 u8 scaler_on;70 u8 scaler_mode;71 u8 compression;72 u8 spr_en;73 u8 spr_mode;74};75 76struct nt35950_panel_desc {77 const char *model_name;78 const struct mipi_dsi_device_info dsi_info;79 const struct nt35950_panel_mode *mode_data;80 81 bool is_dual_dsi;82 u8 num_lanes;83 u8 num_modes;84};85 86static inline struct nt35950 *to_nt35950(struct drm_panel *panel)87{88 return container_of(panel, struct nt35950, panel);89}90 91static void nt35950_reset(struct nt35950 *nt)92{93 gpiod_set_value_cansleep(nt->reset_gpio, 1);94 usleep_range(12000, 13000);95 gpiod_set_value_cansleep(nt->reset_gpio, 0);96 usleep_range(300, 400);97 gpiod_set_value_cansleep(nt->reset_gpio, 1);98 usleep_range(12000, 13000);99}100 101/*102 * nt35950_set_cmd2_page - Select manufacturer control (CMD2) page103 * @dsi_ctx: context for mipi_dsi functions104 * @nt: Main driver structure105 * @page: Page number (0-7)106 */107static void nt35950_set_cmd2_page(struct mipi_dsi_multi_context *dsi_ctx,108 struct nt35950 *nt, u8 page)109{110 const u8 mauc_cmd2_page[] = { MCS_CMD_MAUCCTR, 0x55, 0xaa, 0x52,111 0x08, page };112 113 mipi_dsi_dcs_write_buffer_multi(dsi_ctx, mauc_cmd2_page,114 ARRAY_SIZE(mauc_cmd2_page));115 if (!dsi_ctx->accum_err)116 nt->last_page = page;117}118 119/*120 * nt35950_set_data_compression - Set data compression mode121 * @dsi_ctx: context for mipi_dsi functions122 * @nt: Main driver structure123 * @comp_mode: Compression mode124 */125static void nt35950_set_data_compression(struct mipi_dsi_multi_context *dsi_ctx,126 struct nt35950 *nt, u8 comp_mode)127{128 u8 cmd_data_compression[] = { MCS_PARAM_DATA_COMPRESSION, comp_mode };129 u8 cmd_vesa_dsc_on[] = { MCS_PARAM_VESA_DSC_ON, !!comp_mode };130 u8 cmd_vesa_dsc_setting[] = { MCS_PARAM_VESA_DSC_SETTING, 0x03 };131 u8 last_page = nt->last_page;132 133 /* Set CMD2 Page 0 if we're not there yet */134 if (last_page != 0)135 nt35950_set_cmd2_page(dsi_ctx, nt, 0);136 137 mipi_dsi_dcs_write_buffer_multi(dsi_ctx, cmd_data_compression,138 ARRAY_SIZE(cmd_data_compression));139 mipi_dsi_dcs_write_buffer_multi(dsi_ctx, cmd_vesa_dsc_on,140 ARRAY_SIZE(cmd_vesa_dsc_on));141 142 /* Set the vesa dsc setting on Page 4 */143 nt35950_set_cmd2_page(dsi_ctx, nt, 4);144 145 /* Display Stream Compression setting, always 0x03 */146 mipi_dsi_dcs_write_buffer_multi(dsi_ctx, cmd_vesa_dsc_setting,147 ARRAY_SIZE(cmd_vesa_dsc_setting));148 149 /* Get back to the previously set page */150 nt35950_set_cmd2_page(dsi_ctx, nt, last_page);151}152 153/*154 * nt35950_set_scaler - Enable/disable resolution upscaling155 * @dsi_ctx: context for mipi_dsi functions156 * @scale_up: Scale up function control157 */158static void nt35950_set_scaler(struct mipi_dsi_multi_context *dsi_ctx,159 u8 scale_up)160{161 u8 cmd_scaler[] = { MCS_PARAM_SCALER_FUNCTION, scale_up };162 163 mipi_dsi_dcs_write_buffer_multi(dsi_ctx, cmd_scaler,164 ARRAY_SIZE(cmd_scaler));165}166 167/*168 * nt35950_set_scale_mode - Resolution upscaling mode169 * @dsi_ctx: context for mipi_dsi functions170 * @mode: Scaler mode (MCS_DATA_COMPRESSION_*)171 */172static void nt35950_set_scale_mode(struct mipi_dsi_multi_context *dsi_ctx,173 u8 mode)174{175 u8 cmd_scaler[] = { MCS_PARAM_SCALEUP_MODE, mode };176 177 mipi_dsi_dcs_write_buffer_multi(dsi_ctx, cmd_scaler,178 ARRAY_SIZE(cmd_scaler));179}180 181/*182 * nt35950_inject_black_image - Display a completely black image183 * @dsi_ctx: context for mipi_dsi functions184 *185 * After IC setup, the attached panel may show random data186 * due to driveric behavior changes (resolution, compression,187 * scaling, etc). This function, called after parameters setup,188 * makes the driver ic to output a completely black image to189 * the display.190 * It makes sense to push a black image before sending the sleep-out191 * and display-on commands.192 */193static void nt35950_inject_black_image(struct mipi_dsi_multi_context *dsi_ctx)194{195 const u8 cmd0_black_img[] = { 0x6f, 0x01 };196 const u8 cmd1_black_img[] = { 0xf3, 0x10 };197 u8 cmd_test[] = { 0xff, 0xaa, 0x55, 0xa5, 0x80 };198 199 /* Enable test command */200 mipi_dsi_dcs_write_buffer_multi(dsi_ctx, cmd_test, ARRAY_SIZE(cmd_test));201 202 /* Send a black image */203 mipi_dsi_dcs_write_buffer_multi(dsi_ctx, cmd0_black_img,204 ARRAY_SIZE(cmd0_black_img));205 mipi_dsi_dcs_write_buffer_multi(dsi_ctx, cmd1_black_img,206 ARRAY_SIZE(cmd1_black_img));207 208 /* Disable test command */209 cmd_test[ARRAY_SIZE(cmd_test) - 1] = 0x00;210 mipi_dsi_dcs_write_buffer_multi(dsi_ctx, cmd_test, ARRAY_SIZE(cmd_test));211}212 213/*214 * nt35950_set_dispout - Set Display Output register parameters215 * @nt: Main driver structure216 * @dsi_ctx: context for mipi_dsi functions217 */218static void nt35950_set_dispout(struct mipi_dsi_multi_context *dsi_ctx,219 struct nt35950 *nt)220{221 u8 cmd_dispout[] = { MCS_PARAM_DISP_OUTPUT_CTRL, 0x00 };222 const struct nt35950_panel_mode *mode_data = nt->desc->mode_data;223 224 if (mode_data[nt->cur_mode].is_video_mode)225 cmd_dispout[1] |= MCS_DISP_OUT_VIDEO_MODE;226 if (mode_data[nt->cur_mode].enable_sram)227 cmd_dispout[1] |= MCS_DISP_OUT_SRAM_EN;228 229 mipi_dsi_dcs_write_buffer_multi(dsi_ctx, cmd_dispout,230 ARRAY_SIZE(cmd_dispout));231}232 233static int nt35950_get_current_mode(struct nt35950 *nt)234{235 struct drm_connector *connector = nt->connector;236 struct drm_crtc_state *crtc_state;237 int i;238 239 /* Return the default (first) mode if no info available yet */240 if (!connector->state || !connector->state->crtc)241 return 0;242 243 crtc_state = connector->state->crtc->state;244 245 for (i = 0; i < nt->desc->num_modes; i++) {246 if (drm_mode_match(&crtc_state->mode,247 &nt->desc->mode_data[i].mode,248 DRM_MODE_MATCH_TIMINGS | DRM_MODE_MATCH_CLOCK))249 return i;250 }251 252 return 0;253}254 255static int nt35950_on(struct nt35950 *nt)256{257 const struct nt35950_panel_mode *mode_data = nt->desc->mode_data;258 struct mipi_dsi_device *dsi = nt->dsi[0];259 struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };260 261 nt->cur_mode = nt35950_get_current_mode(nt);262 nt->dsi[0]->mode_flags |= MIPI_DSI_MODE_LPM;263 nt->dsi[1]->mode_flags |= MIPI_DSI_MODE_LPM;264 265 nt35950_set_cmd2_page(&dsi_ctx, nt, 0);266 nt35950_set_data_compression(&dsi_ctx, nt, mode_data[nt->cur_mode].compression);267 nt35950_set_scale_mode(&dsi_ctx, mode_data[nt->cur_mode].scaler_mode);268 nt35950_set_scaler(&dsi_ctx, mode_data[nt->cur_mode].scaler_on);269 nt35950_set_dispout(&dsi_ctx, nt);270 271 mipi_dsi_dcs_set_tear_on_multi(&dsi_ctx, MIPI_DSI_DCS_TEAR_MODE_VBLANK);272 mipi_dsi_dcs_set_tear_scanline_multi(&dsi_ctx, 0);273 274 /* CMD2 Page 1 */275 nt35950_set_cmd2_page(&dsi_ctx, nt, 1);276 277 /* Unknown command */278 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xd4, 0x88, 0x88);279 280 /* CMD2 Page 7 */281 nt35950_set_cmd2_page(&dsi_ctx, nt, 7);282 283 /* Enable SubPixel Rendering */284 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MCS_PARAM_SPR_EN, 0x01);285 286 /* SPR Mode: YYG Rainbow-RGB */287 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MCS_PARAM_SPR_MODE,288 MCS_SPR_MODE_YYG_RAINBOW_RGB);289 290 /* CMD3 */291 nt35950_inject_black_image(&dsi_ctx);292 mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);293 mipi_dsi_msleep(&dsi_ctx, 120);294 295 mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);296 mipi_dsi_msleep(&dsi_ctx, 120);297 298 if (dsi_ctx.accum_err)299 return dsi_ctx.accum_err;300 301 nt->dsi[0]->mode_flags &= ~MIPI_DSI_MODE_LPM;302 nt->dsi[1]->mode_flags &= ~MIPI_DSI_MODE_LPM;303 304 return 0;305}306 307static void nt35950_off(struct nt35950 *nt)308{309 struct mipi_dsi_device *dsi = nt->dsi[0];310 struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };311 312 mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);313 mipi_dsi_usleep_range(&dsi_ctx, 10000, 11000);314 315 mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);316 mipi_dsi_msleep(&dsi_ctx, 150);317 318 nt->dsi[0]->mode_flags |= MIPI_DSI_MODE_LPM;319 nt->dsi[1]->mode_flags |= MIPI_DSI_MODE_LPM;320}321 322static int nt35950_sharp_init_vregs(struct nt35950 *nt, struct device *dev)323{324 int ret;325 326 nt->vregs[0].supply = "vddio";327 nt->vregs[1].supply = "avdd";328 nt->vregs[2].supply = "avee";329 nt->vregs[3].supply = "dvdd";330 ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(nt->vregs),331 nt->vregs);332 if (ret < 0)333 return ret;334 335 ret = regulator_is_supported_voltage(nt->vregs[0].consumer,336 1750000, 1950000);337 if (!ret)338 return -EINVAL;339 ret = regulator_is_supported_voltage(nt->vregs[1].consumer,340 5200000, 5900000);341 if (!ret)342 return -EINVAL;343 /* AVEE is negative: -5.90V to -5.20V */344 ret = regulator_is_supported_voltage(nt->vregs[2].consumer,345 5200000, 5900000);346 if (!ret)347 return -EINVAL;348 349 ret = regulator_is_supported_voltage(nt->vregs[3].consumer,350 1300000, 1400000);351 if (!ret)352 return -EINVAL;353 354 return 0;355}356 357static int nt35950_prepare(struct drm_panel *panel)358{359 struct nt35950 *nt = to_nt35950(panel);360 int ret;361 362 ret = regulator_enable(nt->vregs[0].consumer);363 if (ret)364 return ret;365 usleep_range(2000, 5000);366 367 ret = regulator_enable(nt->vregs[3].consumer);368 if (ret)369 goto end;370 usleep_range(15000, 18000);371 372 ret = regulator_enable(nt->vregs[1].consumer);373 if (ret)374 goto end;375 376 ret = regulator_enable(nt->vregs[2].consumer);377 if (ret)378 goto end;379 usleep_range(12000, 13000);380 381 nt35950_reset(nt);382 383 ret = nt35950_on(nt);384 385end:386 if (ret < 0) {387 regulator_bulk_disable(ARRAY_SIZE(nt->vregs), nt->vregs);388 return ret;389 }390 391 return 0;392}393 394static int nt35950_unprepare(struct drm_panel *panel)395{396 struct nt35950 *nt = to_nt35950(panel);397 398 nt35950_off(nt);399 400 gpiod_set_value_cansleep(nt->reset_gpio, 0);401 regulator_bulk_disable(ARRAY_SIZE(nt->vregs), nt->vregs);402 403 return 0;404}405 406static int nt35950_get_modes(struct drm_panel *panel,407 struct drm_connector *connector)408{409 struct nt35950 *nt = to_nt35950(panel);410 int i;411 412 for (i = 0; i < nt->desc->num_modes; i++) {413 struct drm_display_mode *mode;414 415 mode = drm_mode_duplicate(connector->dev,416 &nt->desc->mode_data[i].mode);417 if (!mode)418 return -ENOMEM;419 420 drm_mode_set_name(mode);421 422 mode->type |= DRM_MODE_TYPE_DRIVER;423 if (nt->desc->num_modes == 1)424 mode->type |= DRM_MODE_TYPE_PREFERRED;425 426 drm_mode_probed_add(connector, mode);427 }428 429 connector->display_info.bpc = 8;430 connector->display_info.height_mm = nt->desc->mode_data[0].mode.height_mm;431 connector->display_info.width_mm = nt->desc->mode_data[0].mode.width_mm;432 nt->connector = connector;433 434 return nt->desc->num_modes;435}436 437static const struct drm_panel_funcs nt35950_panel_funcs = {438 .prepare = nt35950_prepare,439 .unprepare = nt35950_unprepare,440 .get_modes = nt35950_get_modes,441};442 443static int nt35950_probe(struct mipi_dsi_device *dsi)444{445 struct device *dev = &dsi->dev;446 struct device_node *dsi_r;447 struct mipi_dsi_host *dsi_r_host;448 struct nt35950 *nt;449 const struct mipi_dsi_device_info *info;450 int i, num_dsis = 1, ret;451 452 nt = devm_kzalloc(dev, sizeof(*nt), GFP_KERNEL);453 if (!nt)454 return -ENOMEM;455 456 ret = nt35950_sharp_init_vregs(nt, dev);457 if (ret)458 return dev_err_probe(dev, ret, "Regulator init failure.\n");459 460 nt->desc = of_device_get_match_data(dev);461 if (!nt->desc)462 return -ENODEV;463 464 nt->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_ASIS);465 if (IS_ERR(nt->reset_gpio)) {466 return dev_err_probe(dev, PTR_ERR(nt->reset_gpio),467 "Failed to get reset gpio\n");468 }469 470 /* If the panel is connected on two DSIs then DSI0 left, DSI1 right */471 if (nt->desc->is_dual_dsi) {472 info = &nt->desc->dsi_info;473 dsi_r = of_graph_get_remote_node(dsi->dev.of_node, 1, -1);474 if (!dsi_r) {475 dev_err(dev, "Cannot get secondary DSI node.\n");476 return -ENODEV;477 }478 dsi_r_host = of_find_mipi_dsi_host_by_node(dsi_r);479 of_node_put(dsi_r);480 if (!dsi_r_host)481 return dev_err_probe(dev, -EPROBE_DEFER, "Cannot get secondary DSI host\n");482 483 nt->dsi[1] = mipi_dsi_device_register_full(dsi_r_host, info);484 if (!nt->dsi[1]) {485 dev_err(dev, "Cannot get secondary DSI node\n");486 return -ENODEV;487 }488 num_dsis++;489 }490 491 nt->dsi[0] = dsi;492 mipi_dsi_set_drvdata(dsi, nt);493 494 drm_panel_init(&nt->panel, dev, &nt35950_panel_funcs,495 DRM_MODE_CONNECTOR_DSI);496 497 ret = drm_panel_of_backlight(&nt->panel);498 if (ret) {499 if (num_dsis == 2)500 mipi_dsi_device_unregister(nt->dsi[1]);501 502 return dev_err_probe(dev, ret, "Failed to get backlight\n");503 }504 505 drm_panel_add(&nt->panel);506 507 for (i = 0; i < num_dsis; i++) {508 nt->dsi[i]->lanes = nt->desc->num_lanes;509 nt->dsi[i]->format = MIPI_DSI_FMT_RGB888;510 511 nt->dsi[i]->mode_flags = MIPI_DSI_CLOCK_NON_CONTINUOUS |512 MIPI_DSI_MODE_LPM;513 514 if (nt->desc->mode_data[0].is_video_mode)515 nt->dsi[i]->mode_flags |= MIPI_DSI_MODE_VIDEO;516 517 ret = mipi_dsi_attach(nt->dsi[i]);518 if (ret < 0) {519 /* If we fail to attach to either host, we're done */520 if (num_dsis == 2)521 mipi_dsi_device_unregister(nt->dsi[1]);522 523 return dev_err_probe(dev, ret,524 "Cannot attach to DSI%d host.\n", i);525 }526 }527 528 /* Make sure to set RESX LOW before starting the power-on sequence */529 gpiod_set_value_cansleep(nt->reset_gpio, 0);530 return 0;531}532 533static void nt35950_remove(struct mipi_dsi_device *dsi)534{535 struct nt35950 *nt = mipi_dsi_get_drvdata(dsi);536 int ret;537 538 ret = mipi_dsi_detach(nt->dsi[0]);539 if (ret < 0)540 dev_err(&dsi->dev,541 "Failed to detach from DSI0 host: %d\n", ret);542 543 if (nt->dsi[1]) {544 ret = mipi_dsi_detach(nt->dsi[1]);545 if (ret < 0)546 dev_err(&dsi->dev,547 "Failed to detach from DSI1 host: %d\n", ret);548 mipi_dsi_device_unregister(nt->dsi[1]);549 }550 551 drm_panel_remove(&nt->panel);552}553 554static const struct nt35950_panel_mode sharp_ls055d1sx04_modes[] = {555 {556 /* 1920x1080 60Hz no compression */557 .mode = {558 .clock = 214537,559 .hdisplay = 1080,560 .hsync_start = 1080 + 400,561 .hsync_end = 1080 + 400 + 40,562 .htotal = 1080 + 400 + 40 + 300,563 .vdisplay = 1920,564 .vsync_start = 1920 + 12,565 .vsync_end = 1920 + 12 + 2,566 .vtotal = 1920 + 12 + 2 + 10,567 .width_mm = 68,568 .height_mm = 121,569 },570 .compression = MCS_DATA_COMPRESSION_NONE,571 .enable_sram = true,572 .is_video_mode = false,573 .scaler_on = 1,574 .scaler_mode = MCS_SCALEUP_DUPLICATE,575 },576 /* TODO: Add 2160x3840 60Hz when DSC is supported */577};578 579static const struct nt35950_panel_desc sharp_ls055d1sx04 = {580 .model_name = "Sharp LS055D1SX04",581 .dsi_info = {582 .type = "LS055D1SX04",583 .channel = 0,584 .node = NULL,585 },586 .mode_data = sharp_ls055d1sx04_modes,587 .num_modes = ARRAY_SIZE(sharp_ls055d1sx04_modes),588 .is_dual_dsi = true,589 .num_lanes = 4,590};591 592static const struct of_device_id nt35950_of_match[] = {593 { .compatible = "sharp,ls055d1sx04", .data = &sharp_ls055d1sx04 },594 { }595};596MODULE_DEVICE_TABLE(of, nt35950_of_match);597 598static struct mipi_dsi_driver nt35950_driver = {599 .probe = nt35950_probe,600 .remove = nt35950_remove,601 .driver = {602 .name = "panel-novatek-nt35950",603 .of_match_table = nt35950_of_match,604 },605};606module_mipi_dsi_driver(nt35950_driver);607 608MODULE_AUTHOR("AngeloGioacchino Del Regno <angelogioacchino.delregno@somainline.org>");609MODULE_DESCRIPTION("Novatek NT35950 DriverIC panels driver");610MODULE_LICENSE("GPL v2");611