diff options
author | Thierry Reding <treding@nvidia.com> | 2014-08-06 11:10:47 +0200 |
---|---|---|
committer | Thierry Reding <treding@nvidia.com> | 2014-08-07 17:00:28 +0200 |
commit | b4985b82b83ec5ff7766a27aaa38e878dc0686bb (patch) | |
tree | 3d03a8a41c68197a7882b3c0aee25ceb4f74d635 | |
parent | a3b8e0d9a5aa2aba189f792f7edbf6981f087532 (diff) |
drm/tegra: dsi: refactor ganged mode
-rw-r--r-- | drivers/gpu/drm/tegra/dsi.c | 231 |
1 files changed, 123 insertions, 108 deletions
diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c index 70580d8465dc..4d3f89782920 100644 --- a/drivers/gpu/drm/tegra/dsi.c +++ b/drivers/gpu/drm/tegra/dsi.c @@ -567,8 +567,8 @@ static int tegra_dsi_get_format(enum mipi_dsi_pixel_format format, return 0; } -static void tegra_dsi_ganged_split(struct tegra_dsi *dsi, unsigned int start, - unsigned int size) +static void tegra_dsi_ganged_enable(struct tegra_dsi *dsi, unsigned int start, + unsigned int size) { u32 value; @@ -579,27 +579,27 @@ static void tegra_dsi_ganged_split(struct tegra_dsi *dsi, unsigned int start, tegra_dsi_writel(dsi, value, DSI_GANGED_MODE_CONTROL); } -static void tegra_dsi_ganged_disable(struct tegra_dsi *dsi) +static void tegra_dsi_enable(struct tegra_dsi *dsi) { - tegra_dsi_writel(dsi, 0, DSI_GANGED_MODE_START); - tegra_dsi_writel(dsi, 0, DSI_GANGED_MODE_SIZE); - tegra_dsi_writel(dsi, 0, DSI_GANGED_MODE_CONTROL); + u32 value; + + value = tegra_dsi_readl(dsi, DSI_POWER_CONTROL); + value |= DSI_POWER_CONTROL_ENABLE; + tegra_dsi_writel(dsi, value, DSI_POWER_CONTROL); + + if (dsi->slave) + tegra_dsi_enable(dsi->slave); } -static int tegra_output_dsi_enable(struct tegra_output *output) +static int tegra_dsi_configure(struct tegra_dsi *dsi, unsigned int pipe, + const struct drm_display_mode *mode) { - struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc); - struct drm_display_mode *mode = &dc->base.mode; unsigned int hact, hsw, hbp, hfp, i, mul, div; - struct tegra_dsi *dsi = to_dsi(output); enum tegra_dsi_format format; - unsigned long value; const u32 *pkt_seq; + u32 value; int err; - if (dsi->enabled) - return 0; - if (dsi->flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) { DRM_DEBUG_KMS("Non-burst video mode with sync pulses\n"); pkt_seq = pkt_seq_video_non_burst_sync_pulses; @@ -620,7 +620,7 @@ static int tegra_output_dsi_enable(struct tegra_output *output) return err; value = DSI_CONTROL_CHANNEL(0) | DSI_CONTROL_FORMAT(format) | - DSI_CONTROL_SOURCE(dc->pipe); + DSI_CONTROL_SOURCE(pipe); /* FIXME: implement proper check for ganged mode? */ if (dsi->lanes > 4) @@ -630,20 +630,11 @@ static int tegra_output_dsi_enable(struct tegra_output *output) tegra_dsi_writel(dsi, value, DSI_CONTROL); - if (dsi->slave) - tegra_dsi_writel(dsi->slave, value, DSI_CONTROL); - tegra_dsi_writel(dsi, dsi->video_fifo_depth, DSI_MAX_THRESHOLD); - if (dsi->slave) - tegra_dsi_writel(dsi->slave, dsi->slave->video_fifo_depth, DSI_MAX_THRESHOLD); - value = DSI_HOST_CONTROL_HS; tegra_dsi_writel(dsi, value, DSI_HOST_CONTROL); - if (dsi->slave) - tegra_dsi_writel(dsi->slave, value, DSI_HOST_CONTROL); - value = tegra_dsi_readl(dsi, DSI_CONTROL); if (dsi->flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) @@ -661,26 +652,13 @@ static int tegra_output_dsi_enable(struct tegra_output *output) value &= ~DSI_CONTROL_HOST_ENABLE; tegra_dsi_writel(dsi, value, DSI_CONTROL); - if (dsi->slave) - tegra_dsi_writel(dsi->slave, value, DSI_CONTROL); - err = tegra_dsi_set_phy_timing(dsi); if (err < 0) return err; - if (dsi->slave) { - err = tegra_dsi_set_phy_timing(dsi->slave); - if (err < 0) - return err; - } - - for (i = 0; i < NUM_PKT_SEQ; i++) { + for (i = 0; i < NUM_PKT_SEQ; i++) tegra_dsi_writel(dsi, pkt_seq[i], DSI_PKT_SEQ_0_LO + i); - if (dsi->slave) - tegra_dsi_writel(dsi->slave, pkt_seq[i], DSI_PKT_SEQ_0_LO + i); - } - if (dsi->flags & MIPI_DSI_MODE_VIDEO) { /* horizontal active pixels */ hact = mode->hdisplay * mul / div; @@ -694,7 +672,7 @@ static int tegra_output_dsi_enable(struct tegra_output *output) hbp -= 14; /* horizontal front porch */ - hfp = (mode->hsync_start - mode->hdisplay) * mul / div; + hfp = (mode->hsync_start - mode->hdisplay) * mul / div; hfp -= 8; tegra_dsi_writel(dsi, hsw << 16 | 0, DSI_PKT_LEN_0_1); @@ -704,10 +682,12 @@ static int tegra_output_dsi_enable(struct tegra_output *output) /* set SOL delay (for non-burst mode only) */ tegra_dsi_writel(dsi, 8 * mul / div, DSI_SOL_DELAY); + + /* TODO: implement ganged mode */ } else { u16 bytes; - if (dsi->slave) { + if (dsi->ganged_mode) { /* * For ganged mode, assume symmetric left-right mode. */ @@ -722,22 +702,12 @@ static int tegra_output_dsi_enable(struct tegra_output *output) tegra_dsi_writel(dsi, bytes << 16, DSI_PKT_LEN_4_5); tegra_dsi_writel(dsi, 0, DSI_PKT_LEN_6_7); - if (dsi->slave) { - tegra_dsi_writel(dsi->slave, 0, DSI_PKT_LEN_0_1); - tegra_dsi_writel(dsi->slave, bytes << 16, DSI_PKT_LEN_2_3); - tegra_dsi_writel(dsi->slave, bytes << 16, DSI_PKT_LEN_4_5); - tegra_dsi_writel(dsi->slave, 0, DSI_PKT_LEN_6_7); - } - value = MIPI_DCS_WRITE_MEMORY_START << 8 | MIPI_DCS_WRITE_MEMORY_CONTINUE; tegra_dsi_writel(dsi, value, DSI_DCS_CMDS); - if (dsi->slave) - tegra_dsi_writel(dsi->slave, value, DSI_DCS_CMDS); - /* set SOL delay */ - if (dsi->slave) { + if (dsi->ganged_mode) { unsigned long delay, bclk, bclk_ganged; unsigned long lanes = dsi->lanes; @@ -756,18 +726,40 @@ static int tegra_output_dsi_enable(struct tegra_output *output) } tegra_dsi_writel(dsi, value, DSI_SOL_DELAY); + } - if (dsi->slave) - tegra_dsi_writel(dsi->slave, value, DSI_SOL_DELAY); + if (dsi->slave) { + err = tegra_dsi_configure(dsi->slave, pipe, mode); + if (err < 0) + return err; - if (dsi->slave) { - /* symmetrical left-right split */ - tegra_dsi_ganged_split(dsi, 0, mode->hdisplay / 2); - tegra_dsi_ganged_split(dsi->slave, mode->hdisplay / 2, - mode->hdisplay / 2); - } + /* + * TODO: Support modes other than symmetrical left-right + * split. + */ + tegra_dsi_ganged_enable(dsi, 0, mode->hdisplay / 2); + tegra_dsi_ganged_enable(dsi->slave, mode->hdisplay / 2, + mode->hdisplay / 2); } + return 0; +} + +static int tegra_output_dsi_enable(struct tegra_output *output) +{ + struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc); + const struct drm_display_mode *mode = &dc->base.mode; + struct tegra_dsi *dsi = to_dsi(output); + u32 value; + int err; + + if (dsi->enabled) + return 0; + + err = tegra_dsi_configure(dsi, dc->pipe, mode); + if (err < 0) + return err; + /* enable display controller */ value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); value |= DSI_ENABLE; @@ -787,12 +779,7 @@ static int tegra_output_dsi_enable(struct tegra_output *output) tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); /* enable DSI controller */ - value = tegra_dsi_readl(dsi, DSI_POWER_CONTROL); - value |= DSI_POWER_CONTROL_ENABLE; - tegra_dsi_writel(dsi, value, DSI_POWER_CONTROL); - - if (dsi->slave) - tegra_dsi_writel(dsi->slave, value, DSI_POWER_CONTROL); + tegra_dsi_enable(dsi); dsi->enabled = true; @@ -816,6 +803,44 @@ static int tegra_dsi_wait_idle(struct tegra_dsi *dsi, unsigned long timeout) return -ETIMEDOUT; } +static void tegra_dsi_video_disable(struct tegra_dsi *dsi) +{ + u32 value; + + value = tegra_dsi_readl(dsi, DSI_CONTROL); + value &= ~DSI_CONTROL_VIDEO_ENABLE; + tegra_dsi_writel(dsi, value, DSI_CONTROL); + + if (dsi->slave) + tegra_dsi_video_disable(dsi->slave); +} + +static void tegra_dsi_ganged_disable(struct tegra_dsi *dsi) +{ + tegra_dsi_writel(dsi, 0, DSI_GANGED_MODE_START); + tegra_dsi_writel(dsi, 0, DSI_GANGED_MODE_SIZE); + tegra_dsi_writel(dsi, 0, DSI_GANGED_MODE_CONTROL); +} + +static void tegra_dsi_disable(struct tegra_dsi *dsi) +{ + u32 value; + + if (dsi->slave) { + tegra_dsi_ganged_disable(dsi->slave); + tegra_dsi_ganged_disable(dsi); + } + + value = tegra_dsi_readl(dsi, DSI_POWER_CONTROL); + value &= ~DSI_POWER_CONTROL_ENABLE; + tegra_dsi_writel(dsi, value, DSI_POWER_CONTROL); + + if (dsi->slave) + tegra_dsi_disable(dsi->slave); + + usleep_range(5000, 10000); +} + static int tegra_output_dsi_disable(struct tegra_output *output) { struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc); @@ -826,13 +851,7 @@ static int tegra_output_dsi_disable(struct tegra_output *output) if (!dsi->enabled) return 0; - /* disable video DSI interface */ - value = tegra_dsi_readl(dsi, DSI_CONTROL); - value &= ~DSI_CONTROL_VIDEO_ENABLE; - tegra_dsi_writel(dsi, value, DSI_CONTROL); - - if (dsi->slave) - tegra_dsi_writel(dsi->slave, value, DSI_CONTROL); + tegra_dsi_video_disable(dsi); /* * The following accesses registers of the display controller, so make @@ -860,23 +879,34 @@ static int tegra_output_dsi_disable(struct tegra_output *output) if (err < 0) dev_dbg(dsi->dev, "failed to idle DSI: %d\n", err); - /* disable DSI controller */ - value = tegra_dsi_readl(dsi, DSI_POWER_CONTROL); - value &= ~DSI_POWER_CONTROL_ENABLE; - tegra_dsi_writel(dsi, value, DSI_POWER_CONTROL); + tegra_dsi_disable(dsi); - if (dsi->slave) { - tegra_dsi_writel(dsi->slave, value, DSI_POWER_CONTROL); + dsi->enabled = false; - tegra_dsi_ganged_disable(dsi->slave); - tegra_dsi_ganged_disable(dsi); - } + return 0; +} - usleep_range(5000, 10000); +static void tegra_dsi_set_timeout(struct tegra_dsi *dsi, unsigned long bclk, + unsigned int vrefresh) +{ + unsigned int timeout; + u32 value; - dsi->enabled = false; + /* one frame high-speed transmission timeout */ + timeout = (bclk / vrefresh) / 512; + value = DSI_TIMEOUT_LRX(0x2000) | DSI_TIMEOUT_HTX(timeout); + tegra_dsi_writel(dsi, value, DSI_TIMEOUT_0); - return 0; + /* 2 ms peripheral timeout for panel */ + timeout = 2 * bclk / 512 * 1000; + value = DSI_TIMEOUT_PR(timeout) | DSI_TIMEOUT_TA(0x2000); + tegra_dsi_writel(dsi, value, DSI_TIMEOUT_1); + + value = DSI_TALLY_TA(0) | DSI_TALLY_LRX(0) | DSI_TALLY_HTX(0); + tegra_dsi_writel(dsi, value, DSI_TO_TALLY); + + if (dsi->slave) + tegra_dsi_set_timeout(dsi->slave, bclk, vrefresh); } static int tegra_output_dsi_setup_clock(struct tegra_output *output, @@ -885,9 +915,9 @@ static int tegra_output_dsi_setup_clock(struct tegra_output *output, { struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc); struct drm_display_mode *mode = &dc->base.mode; - unsigned int timeout, mul, div, vrefresh; struct tegra_dsi *dsi = to_dsi(output); - unsigned long bclk, plld, value; + unsigned int mul, div, vrefresh; + unsigned long bclk, plld; int err; err = tegra_dsi_get_muldiv(dsi->format, &mul, &div); @@ -942,28 +972,7 @@ static int tegra_output_dsi_setup_clock(struct tegra_output *output, * XXX: Move the below somewhere else so that we don't need to have * access to the vrefresh in this function? */ - - /* one frame high-speed transmission timeout */ - timeout = (bclk / vrefresh) / 512; - value = DSI_TIMEOUT_LRX(0x2000) | DSI_TIMEOUT_HTX(timeout); - tegra_dsi_writel(dsi, value, DSI_TIMEOUT_0); - - if (dsi->slave) - tegra_dsi_writel(dsi->slave, value, DSI_TIMEOUT_0); - - /* 2 ms peripheral timeout for panel */ - timeout = 2 * bclk / 512 * 1000; - value = DSI_TIMEOUT_PR(timeout) | DSI_TIMEOUT_TA(0x2000); - tegra_dsi_writel(dsi, value, DSI_TIMEOUT_1); - - if (dsi->slave) - tegra_dsi_writel(dsi->slave, value, DSI_TIMEOUT_1); - - value = DSI_TALLY_TA(0) | DSI_TALLY_LRX(0) | DSI_TALLY_HTX(0); - tegra_dsi_writel(dsi, value, DSI_TO_TALLY); - - if (dsi->slave) - tegra_dsi_writel(dsi->slave, value, DSI_TO_TALLY); + tegra_dsi_set_timeout(dsi, bclk, vrefresh); return 0; } @@ -1356,6 +1365,12 @@ static int tegra_dsi_host_attach(struct mipi_dsi_host *host, dsi->format = device->format; dsi->lanes = device->lanes; + if (dsi->slave) { + dsi->slave->flags = dsi->flags; + dsi->slave->format = dsi->format; + dsi->slave->lanes = dsi->lanes; + } + output->panel = of_drm_find_panel(device->dev.of_node); if (output->panel) { if (output->connector.dev) { |