aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/tegra/dsi.c
diff options
context:
space:
mode:
authorThierry Reding <treding@nvidia.com>2014-11-13 09:02:46 -0500
committerThierry Reding <treding@nvidia.com>2014-11-13 10:12:23 -0500
commit337b443d58e2d7d04d23ed07ff61b1243d5f9f2d (patch)
treeae43d405987adfb06934e52e9ac1e030b23cba04 /drivers/gpu/drm/tegra/dsi.c
parent563eff1f989917779d8db4c5208e12adcbfcf655 (diff)
drm/tegra: dsi: Add command mode support
Add support for DC-driven command mode. This is a mode where the video stream sent by the display controller is packed into DCS command packets (write_memory_start and write_memory_continue) by the DSI controller. It can be used for panels with a remote framebuffer and is useful to save power when used with a dynamic refresh rate (not yet supported by the driver). Signed-off-by: Thierry Reding <treding@nvidia.com>
Diffstat (limited to 'drivers/gpu/drm/tegra/dsi.c')
-rw-r--r--drivers/gpu/drm/tegra/dsi.c82
1 files changed, 63 insertions, 19 deletions
diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c
index b91d9e4016bc..50684a4aa4f0 100644
--- a/drivers/gpu/drm/tegra/dsi.c
+++ b/drivers/gpu/drm/tegra/dsi.c
@@ -318,6 +318,21 @@ static const u32 pkt_seq_video_non_burst_sync_events[NUM_PKT_SEQ] = {
318 [11] = PKT_ID0(MIPI_DSI_BLANKING_PACKET) | PKT_LEN0(4), 318 [11] = PKT_ID0(MIPI_DSI_BLANKING_PACKET) | PKT_LEN0(4),
319}; 319};
320 320
321static const u32 pkt_seq_command_mode[NUM_PKT_SEQ] = {
322 [ 0] = 0,
323 [ 1] = 0,
324 [ 2] = 0,
325 [ 3] = 0,
326 [ 4] = 0,
327 [ 5] = 0,
328 [ 6] = PKT_ID0(MIPI_DSI_DCS_LONG_WRITE) | PKT_LEN0(3) | PKT_LP,
329 [ 7] = 0,
330 [ 8] = 0,
331 [ 9] = 0,
332 [10] = PKT_ID0(MIPI_DSI_DCS_LONG_WRITE) | PKT_LEN0(5) | PKT_LP,
333 [11] = 0,
334};
335
321static int tegra_dsi_set_phy_timing(struct tegra_dsi *dsi) 336static int tegra_dsi_set_phy_timing(struct tegra_dsi *dsi)
322{ 337{
323 struct mipi_dphy_timing timing; 338 struct mipi_dphy_timing timing;
@@ -447,9 +462,12 @@ static int tegra_dsi_configure(struct tegra_dsi *dsi, unsigned int pipe,
447 if (dsi->flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) { 462 if (dsi->flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) {
448 DRM_DEBUG_KMS("Non-burst video mode with sync pulses\n"); 463 DRM_DEBUG_KMS("Non-burst video mode with sync pulses\n");
449 pkt_seq = pkt_seq_video_non_burst_sync_pulses; 464 pkt_seq = pkt_seq_video_non_burst_sync_pulses;
450 } else { 465 } else if (dsi->flags & MIPI_DSI_MODE_VIDEO) {
451 DRM_DEBUG_KMS("Non-burst video mode with sync events\n"); 466 DRM_DEBUG_KMS("Non-burst video mode with sync events\n");
452 pkt_seq = pkt_seq_video_non_burst_sync_events; 467 pkt_seq = pkt_seq_video_non_burst_sync_events;
468 } else {
469 DRM_DEBUG_KMS("Command mode\n");
470 pkt_seq = pkt_seq_command_mode;
453 } 471 }
454 472
455 err = tegra_dsi_get_muldiv(dsi->format, &mul, &div); 473 err = tegra_dsi_get_muldiv(dsi->format, &mul, &div);
@@ -476,7 +494,13 @@ static int tegra_dsi_configure(struct tegra_dsi *dsi, unsigned int pipe,
476 value |= DSI_CONTROL_HS_CLK_CTRL; 494 value |= DSI_CONTROL_HS_CLK_CTRL;
477 495
478 value &= ~DSI_CONTROL_TX_TRIG(3); 496 value &= ~DSI_CONTROL_TX_TRIG(3);
479 value &= ~DSI_CONTROL_DCS_ENABLE; 497
498 /* enable DCS commands for command mode */
499 if (dsi->flags & MIPI_DSI_MODE_VIDEO)
500 value &= ~DSI_CONTROL_DCS_ENABLE;
501 else
502 value |= DSI_CONTROL_DCS_ENABLE;
503
480 value |= DSI_CONTROL_VIDEO_ENABLE; 504 value |= DSI_CONTROL_VIDEO_ENABLE;
481 value &= ~DSI_CONTROL_HOST_ENABLE; 505 value &= ~DSI_CONTROL_HOST_ENABLE;
482 tegra_dsi_writel(dsi, value, DSI_CONTROL); 506 tegra_dsi_writel(dsi, value, DSI_CONTROL);
@@ -488,28 +512,48 @@ static int tegra_dsi_configure(struct tegra_dsi *dsi, unsigned int pipe,
488 for (i = 0; i < NUM_PKT_SEQ; i++) 512 for (i = 0; i < NUM_PKT_SEQ; i++)
489 tegra_dsi_writel(dsi, pkt_seq[i], DSI_PKT_SEQ_0_LO + i); 513 tegra_dsi_writel(dsi, pkt_seq[i], DSI_PKT_SEQ_0_LO + i);
490 514
491 /* horizontal active pixels */ 515 if (dsi->flags & MIPI_DSI_MODE_VIDEO) {
492 hact = mode->hdisplay * mul / div; 516 /* horizontal active pixels */
517 hact = mode->hdisplay * mul / div;
518
519 /* horizontal sync width */
520 hsw = (mode->hsync_end - mode->hsync_start) * mul / div;
521 hsw -= 10;
522
523 /* horizontal back porch */
524 hbp = (mode->htotal - mode->hsync_end) * mul / div;
525 hbp -= 14;
526
527 /* horizontal front porch */
528 hfp = (mode->hsync_start - mode->hdisplay) * mul / div;
529 hfp -= 8;
493 530
494 /* horizontal sync width */ 531 tegra_dsi_writel(dsi, hsw << 16 | 0, DSI_PKT_LEN_0_1);
495 hsw = (mode->hsync_end - mode->hsync_start) * mul / div; 532 tegra_dsi_writel(dsi, hact << 16 | hbp, DSI_PKT_LEN_2_3);
496 hsw -= 10; 533 tegra_dsi_writel(dsi, hfp, DSI_PKT_LEN_4_5);
534 tegra_dsi_writel(dsi, 0x0f0f << 16, DSI_PKT_LEN_6_7);
497 535
498 /* horizontal back porch */ 536 /* set SOL delay (for non-burst mode only) */
499 hbp = (mode->htotal - mode->hsync_end) * mul / div; 537 tegra_dsi_writel(dsi, 8 * mul / div, DSI_SOL_DELAY);
500 hbp -= 14; 538 } else {
539 u16 bytes;
540
541 /* 1 byte (DCS command) + pixel data */
542 bytes = 1 + mode->hdisplay * mul / div;
501 543
502 /* horizontal front porch */ 544 tegra_dsi_writel(dsi, 0, DSI_PKT_LEN_0_1);
503 hfp = (mode->hsync_start - mode->hdisplay) * mul / div; 545 tegra_dsi_writel(dsi, bytes << 16, DSI_PKT_LEN_2_3);
504 hfp -= 8; 546 tegra_dsi_writel(dsi, bytes << 16, DSI_PKT_LEN_4_5);
547 tegra_dsi_writel(dsi, 0, DSI_PKT_LEN_6_7);
505 548
506 tegra_dsi_writel(dsi, hsw << 16 | 0, DSI_PKT_LEN_0_1); 549 value = MIPI_DCS_WRITE_MEMORY_START << 8 |
507 tegra_dsi_writel(dsi, hact << 16 | hbp, DSI_PKT_LEN_2_3); 550 MIPI_DCS_WRITE_MEMORY_CONTINUE;
508 tegra_dsi_writel(dsi, hfp, DSI_PKT_LEN_4_5); 551 tegra_dsi_writel(dsi, value, DSI_DCS_CMDS);
509 tegra_dsi_writel(dsi, 0x0f0f << 16, DSI_PKT_LEN_6_7);
510 552
511 /* set SOL delay (for non-burst mode only) */ 553 value = 8 * mul / div;
512 tegra_dsi_writel(dsi, 8 * mul / div, DSI_SOL_DELAY); 554
555 tegra_dsi_writel(dsi, value, DSI_SOL_DELAY);
556 }
513 557
514 return 0; 558 return 0;
515} 559}