Commit ebd14afe authored by Thierry Reding's avatar Thierry Reding

drm/tegra: dsi: Implement ->atomic_check()

The implementation of the ->atomic_check() callback precomputes all
parameters to check if the given configuration can be applied. If so the
precomputed values are stored in the atomic state object for the encoder
and applied during modeset. In that way the modeset no longer needs to
perform any checking but simply program values into registers.
Signed-off-by: default avatarThierry Reding <treding@nvidia.com>
parent 3cebae67
...@@ -28,6 +28,28 @@ ...@@ -28,6 +28,28 @@
#include "dsi.h" #include "dsi.h"
#include "mipi-phy.h" #include "mipi-phy.h"
struct tegra_dsi_state {
struct drm_connector_state base;
struct mipi_dphy_timing timing;
unsigned long period;
unsigned int vrefresh;
unsigned int lanes;
unsigned long pclk;
unsigned long bclk;
enum tegra_dsi_format format;
unsigned int mul;
unsigned int div;
};
static inline struct tegra_dsi_state *
to_dsi_state(struct drm_connector_state *state)
{
return container_of(state, struct tegra_dsi_state, base);
}
struct tegra_dsi { struct tegra_dsi {
struct host1x_client client; struct host1x_client client;
struct tegra_output output; struct tegra_output output;
...@@ -77,6 +99,11 @@ static inline struct tegra_dsi *to_dsi(struct tegra_output *output) ...@@ -77,6 +99,11 @@ static inline struct tegra_dsi *to_dsi(struct tegra_output *output)
return container_of(output, struct tegra_dsi, output); return container_of(output, struct tegra_dsi, output);
} }
static struct tegra_dsi_state *tegra_dsi_get_state(struct tegra_dsi *dsi)
{
return to_dsi_state(dsi->output.connector.state);
}
static inline u32 tegra_dsi_readl(struct tegra_dsi *dsi, unsigned long reg) static inline u32 tegra_dsi_readl(struct tegra_dsi *dsi, unsigned long reg)
{ {
return readl(dsi->regs + (reg << 2)); return readl(dsi->regs + (reg << 2));
...@@ -335,62 +362,36 @@ static const u32 pkt_seq_command_mode[NUM_PKT_SEQ] = { ...@@ -335,62 +362,36 @@ static const u32 pkt_seq_command_mode[NUM_PKT_SEQ] = {
[11] = 0, [11] = 0,
}; };
static int tegra_dsi_set_phy_timing(struct tegra_dsi *dsi) static void tegra_dsi_set_phy_timing(struct tegra_dsi *dsi,
unsigned long period,
const struct mipi_dphy_timing *timing)
{ {
struct mipi_dphy_timing timing;
unsigned long period;
u32 value; u32 value;
long rate;
int err;
rate = clk_get_rate(dsi->clk);
if (rate < 0)
return rate;
period = DIV_ROUND_CLOSEST(NSEC_PER_SEC, rate * 2);
err = mipi_dphy_timing_get_default(&timing, period); value = DSI_TIMING_FIELD(timing->hsexit, period, 1) << 24 |
if (err < 0) DSI_TIMING_FIELD(timing->hstrail, period, 0) << 16 |
return err; DSI_TIMING_FIELD(timing->hszero, period, 3) << 8 |
DSI_TIMING_FIELD(timing->hsprepare, period, 1);
err = mipi_dphy_timing_validate(&timing, period);
if (err < 0) {
dev_err(dsi->dev, "failed to validate D-PHY timing: %d\n", err);
return err;
}
/*
* The D-PHY timing fields below are expressed in byte-clock cycles,
* so multiply the period by 8.
*/
period *= 8;
value = DSI_TIMING_FIELD(timing.hsexit, period, 1) << 24 |
DSI_TIMING_FIELD(timing.hstrail, period, 0) << 16 |
DSI_TIMING_FIELD(timing.hszero, period, 3) << 8 |
DSI_TIMING_FIELD(timing.hsprepare, period, 1);
tegra_dsi_writel(dsi, value, DSI_PHY_TIMING_0); tegra_dsi_writel(dsi, value, DSI_PHY_TIMING_0);
value = DSI_TIMING_FIELD(timing.clktrail, period, 1) << 24 | value = DSI_TIMING_FIELD(timing->clktrail, period, 1) << 24 |
DSI_TIMING_FIELD(timing.clkpost, period, 1) << 16 | DSI_TIMING_FIELD(timing->clkpost, period, 1) << 16 |
DSI_TIMING_FIELD(timing.clkzero, period, 1) << 8 | DSI_TIMING_FIELD(timing->clkzero, period, 1) << 8 |
DSI_TIMING_FIELD(timing.lpx, period, 1); DSI_TIMING_FIELD(timing->lpx, period, 1);
tegra_dsi_writel(dsi, value, DSI_PHY_TIMING_1); tegra_dsi_writel(dsi, value, DSI_PHY_TIMING_1);
value = DSI_TIMING_FIELD(timing.clkprepare, period, 1) << 16 | value = DSI_TIMING_FIELD(timing->clkprepare, period, 1) << 16 |
DSI_TIMING_FIELD(timing.clkpre, period, 1) << 8 | DSI_TIMING_FIELD(timing->clkpre, period, 1) << 8 |
DSI_TIMING_FIELD(0xff * period, period, 0) << 0; DSI_TIMING_FIELD(0xff * period, period, 0) << 0;
tegra_dsi_writel(dsi, value, DSI_PHY_TIMING_2); tegra_dsi_writel(dsi, value, DSI_PHY_TIMING_2);
value = DSI_TIMING_FIELD(timing.taget, period, 1) << 16 | value = DSI_TIMING_FIELD(timing->taget, period, 1) << 16 |
DSI_TIMING_FIELD(timing.tasure, period, 1) << 8 | DSI_TIMING_FIELD(timing->tasure, period, 1) << 8 |
DSI_TIMING_FIELD(timing.tago, period, 1); DSI_TIMING_FIELD(timing->tago, period, 1);
tegra_dsi_writel(dsi, value, DSI_BTA_TIMING); tegra_dsi_writel(dsi, value, DSI_BTA_TIMING);
if (dsi->slave) if (dsi->slave)
return tegra_dsi_set_phy_timing(dsi->slave); tegra_dsi_set_phy_timing(dsi->slave, period, timing);
return 0;
} }
static int tegra_dsi_get_muldiv(enum mipi_dsi_pixel_format format, static int tegra_dsi_get_muldiv(enum mipi_dsi_pixel_format format,
...@@ -482,14 +483,22 @@ static unsigned int tegra_dsi_get_lanes(struct tegra_dsi *dsi) ...@@ -482,14 +483,22 @@ static unsigned int tegra_dsi_get_lanes(struct tegra_dsi *dsi)
return dsi->lanes; return dsi->lanes;
} }
static int tegra_dsi_configure(struct tegra_dsi *dsi, unsigned int pipe, static void tegra_dsi_configure(struct tegra_dsi *dsi, unsigned int pipe,
const struct drm_display_mode *mode) const struct drm_display_mode *mode)
{ {
unsigned int hact, hsw, hbp, hfp, i, mul, div; unsigned int hact, hsw, hbp, hfp, i, mul, div;
enum tegra_dsi_format format; struct tegra_dsi_state *state;
const u32 *pkt_seq; const u32 *pkt_seq;
u32 value; u32 value;
int err;
/* XXX: pass in state into this function? */
if (dsi->master)
state = tegra_dsi_get_state(dsi->master);
else
state = tegra_dsi_get_state(dsi);
mul = state->mul;
div = state->div;
if (dsi->flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) { if (dsi->flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) {
DRM_DEBUG_KMS("Non-burst video mode with sync pulses\n"); DRM_DEBUG_KMS("Non-burst video mode with sync pulses\n");
...@@ -502,15 +511,8 @@ static int tegra_dsi_configure(struct tegra_dsi *dsi, unsigned int pipe, ...@@ -502,15 +511,8 @@ static int tegra_dsi_configure(struct tegra_dsi *dsi, unsigned int pipe,
pkt_seq = pkt_seq_command_mode; pkt_seq = pkt_seq_command_mode;
} }
err = tegra_dsi_get_muldiv(dsi->format, &mul, &div); value = DSI_CONTROL_CHANNEL(0) |
if (err < 0) DSI_CONTROL_FORMAT(state->format) |
return err;
err = tegra_dsi_get_format(dsi->format, &format);
if (err < 0)
return err;
value = DSI_CONTROL_CHANNEL(0) | DSI_CONTROL_FORMAT(format) |
DSI_CONTROL_LANES(dsi->lanes - 1) | DSI_CONTROL_LANES(dsi->lanes - 1) |
DSI_CONTROL_SOURCE(pipe); DSI_CONTROL_SOURCE(pipe);
tegra_dsi_writel(dsi, value, DSI_CONTROL); tegra_dsi_writel(dsi, value, DSI_CONTROL);
...@@ -589,8 +591,8 @@ static int tegra_dsi_configure(struct tegra_dsi *dsi, unsigned int pipe, ...@@ -589,8 +591,8 @@ static int tegra_dsi_configure(struct tegra_dsi *dsi, unsigned int pipe,
/* set SOL delay */ /* set SOL delay */
if (dsi->master || dsi->slave) { if (dsi->master || dsi->slave) {
unsigned int lanes = tegra_dsi_get_lanes(dsi);
unsigned long delay, bclk, bclk_ganged; unsigned long delay, bclk, bclk_ganged;
unsigned int lanes = state->lanes;
/* SOL to valid, valid to FIFO and FIFO write delay */ /* SOL to valid, valid to FIFO and FIFO write delay */
delay = 4 + 4 + 2; delay = 4 + 4 + 2;
...@@ -610,9 +612,7 @@ static int tegra_dsi_configure(struct tegra_dsi *dsi, unsigned int pipe, ...@@ -610,9 +612,7 @@ static int tegra_dsi_configure(struct tegra_dsi *dsi, unsigned int pipe,
} }
if (dsi->slave) { if (dsi->slave) {
err = tegra_dsi_configure(dsi->slave, pipe, mode); tegra_dsi_configure(dsi->slave, pipe, mode);
if (err < 0)
return err;
/* /*
* TODO: Support modes other than symmetrical left-right * TODO: Support modes other than symmetrical left-right
...@@ -622,8 +622,6 @@ static int tegra_dsi_configure(struct tegra_dsi *dsi, unsigned int pipe, ...@@ -622,8 +622,6 @@ static int tegra_dsi_configure(struct tegra_dsi *dsi, unsigned int pipe,
tegra_dsi_ganged_enable(dsi->slave, mode->hdisplay / 2, tegra_dsi_ganged_enable(dsi->slave, mode->hdisplay / 2,
mode->hdisplay / 2); mode->hdisplay / 2);
} }
return 0;
} }
static int tegra_dsi_wait_idle(struct tegra_dsi *dsi, unsigned long timeout) static int tegra_dsi_wait_idle(struct tegra_dsi *dsi, unsigned long timeout)
...@@ -732,13 +730,38 @@ static void tegra_dsi_connector_dpms(struct drm_connector *connector, int mode) ...@@ -732,13 +730,38 @@ static void tegra_dsi_connector_dpms(struct drm_connector *connector, int mode)
{ {
} }
static void tegra_dsi_connector_reset(struct drm_connector *connector)
{
struct tegra_dsi_state *state;
kfree(connector->state);
connector->state = NULL;
state = kzalloc(sizeof(*state), GFP_KERNEL);
if (state)
connector->state = &state->base;
}
static struct drm_connector_state *
tegra_dsi_connector_duplicate_state(struct drm_connector *connector)
{
struct tegra_dsi_state *state = to_dsi_state(connector->state);
struct tegra_dsi_state *copy;
copy = kmemdup(state, sizeof(*state), GFP_KERNEL);
if (!copy)
return NULL;
return &copy->base;
}
static const struct drm_connector_funcs tegra_dsi_connector_funcs = { static const struct drm_connector_funcs tegra_dsi_connector_funcs = {
.dpms = tegra_dsi_connector_dpms, .dpms = tegra_dsi_connector_dpms,
.reset = drm_atomic_helper_connector_reset, .reset = tegra_dsi_connector_reset,
.detect = tegra_output_connector_detect, .detect = tegra_output_connector_detect,
.fill_modes = drm_helper_probe_single_connector_modes, .fill_modes = drm_helper_probe_single_connector_modes,
.destroy = tegra_output_connector_destroy, .destroy = tegra_output_connector_destroy,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, .atomic_duplicate_state = tegra_dsi_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
}; };
...@@ -771,7 +794,9 @@ static bool tegra_dsi_encoder_mode_fixup(struct drm_encoder *encoder, ...@@ -771,7 +794,9 @@ static bool tegra_dsi_encoder_mode_fixup(struct drm_encoder *encoder,
struct tegra_dc *dc = to_tegra_dc(encoder->crtc); struct tegra_dc *dc = to_tegra_dc(encoder->crtc);
unsigned int mul, div, scdiv, vrefresh, lanes; unsigned int mul, div, scdiv, vrefresh, lanes;
struct tegra_dsi *dsi = to_dsi(output); struct tegra_dsi *dsi = to_dsi(output);
struct mipi_dphy_timing timing;
unsigned long pclk, bclk, plld; unsigned long pclk, bclk, plld;
unsigned long period;
int err; int err;
lanes = tegra_dsi_get_lanes(dsi); lanes = tegra_dsi_get_lanes(dsi);
...@@ -792,6 +817,7 @@ static bool tegra_dsi_encoder_mode_fixup(struct drm_encoder *encoder, ...@@ -792,6 +817,7 @@ static bool tegra_dsi_encoder_mode_fixup(struct drm_encoder *encoder,
* Compute bit clock and round up to the next MHz. * Compute bit clock and round up to the next MHz.
*/ */
plld = DIV_ROUND_UP(bclk * 8, USEC_PER_SEC) * USEC_PER_SEC; plld = DIV_ROUND_UP(bclk * 8, USEC_PER_SEC) * USEC_PER_SEC;
period = DIV_ROUND_CLOSEST(NSEC_PER_SEC, plld);
/* /*
* We divide the frequency by two here, but we make up for that by * We divide the frequency by two here, but we make up for that by
...@@ -827,12 +853,22 @@ static bool tegra_dsi_encoder_mode_fixup(struct drm_encoder *encoder, ...@@ -827,12 +853,22 @@ static bool tegra_dsi_encoder_mode_fixup(struct drm_encoder *encoder,
tegra_dsi_set_timeout(dsi, bclk, vrefresh); tegra_dsi_set_timeout(dsi, bclk, vrefresh);
err = tegra_dsi_set_phy_timing(dsi); err = mipi_dphy_timing_get_default(&timing, period);
if (err < 0)
return err;
err = mipi_dphy_timing_validate(&timing, period);
if (err < 0) { if (err < 0) {
dev_err(dsi->dev, "failed to setup D-PHY timing: %d\n", err); dev_err(dsi->dev, "failed to validate D-PHY timing: %d\n", err);
return false; return err;
} }
/*
* The D-PHY timing fields are expressed in byte-clock cycles, so
* multiply the period by 8.
*/
tegra_dsi_set_phy_timing(dsi, period * 8, &timing);
return true; return true;
} }
...@@ -851,19 +887,24 @@ static void tegra_dsi_encoder_mode_set(struct drm_encoder *encoder, ...@@ -851,19 +887,24 @@ static void tegra_dsi_encoder_mode_set(struct drm_encoder *encoder,
struct tegra_output *output = encoder_to_output(encoder); struct tegra_output *output = encoder_to_output(encoder);
struct tegra_dc *dc = to_tegra_dc(encoder->crtc); struct tegra_dc *dc = to_tegra_dc(encoder->crtc);
struct tegra_dsi *dsi = to_dsi(output); struct tegra_dsi *dsi = to_dsi(output);
struct tegra_dsi_state *state;
u32 value; u32 value;
int err;
state = tegra_dsi_get_state(dsi);
err = tegra_dsi_configure(dsi, dc->pipe, mode); tegra_dsi_set_timeout(dsi, state->bclk, state->vrefresh);
if (err < 0) {
dev_err(dsi->dev, "failed to configure DSI: %d\n", err); /*
return; * The D-PHY timing fields are expressed in byte-clock cycles, so
} * multiply the period by 8.
*/
tegra_dsi_set_phy_timing(dsi, state->period * 8, &state->timing);
if (output->panel) if (output->panel)
drm_panel_prepare(output->panel); drm_panel_prepare(output->panel);
tegra_dsi_configure(dsi, dc->pipe, mode);
/* enable display controller */ /* enable display controller */
value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
value |= DSI_ENABLE; value |= DSI_ENABLE;
...@@ -929,6 +970,87 @@ static void tegra_dsi_encoder_disable(struct drm_encoder *encoder) ...@@ -929,6 +970,87 @@ static void tegra_dsi_encoder_disable(struct drm_encoder *encoder)
return; return;
} }
static int
tegra_dsi_encoder_atomic_check(struct drm_encoder *encoder,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
{
struct tegra_output *output = encoder_to_output(encoder);
struct tegra_dsi_state *state = to_dsi_state(conn_state);
struct tegra_dc *dc = to_tegra_dc(conn_state->crtc);
struct tegra_dsi *dsi = to_dsi(output);
unsigned int scdiv;
unsigned long plld;
int err;
state->pclk = crtc_state->mode.clock * 1000;
err = tegra_dsi_get_muldiv(dsi->format, &state->mul, &state->div);
if (err < 0)
return err;
state->lanes = tegra_dsi_get_lanes(dsi);
err = tegra_dsi_get_format(dsi->format, &state->format);
if (err < 0)
return err;
state->vrefresh = drm_mode_vrefresh(&crtc_state->mode);
/* compute byte clock */
state->bclk = (state->pclk * state->mul) / (state->div * state->lanes);
DRM_DEBUG_KMS("mul: %u, div: %u, lanes: %u\n", state->mul, state->div,
state->lanes);
DRM_DEBUG_KMS("format: %u, vrefresh: %u\n", state->format,
state->vrefresh);
DRM_DEBUG_KMS("bclk: %lu\n", state->bclk);
/*
* Compute bit clock and round up to the next MHz.
*/
plld = DIV_ROUND_UP(state->bclk * 8, USEC_PER_SEC) * USEC_PER_SEC;
state->period = DIV_ROUND_CLOSEST(NSEC_PER_SEC, plld);
err = mipi_dphy_timing_get_default(&state->timing, state->period);
if (err < 0)
return err;
err = mipi_dphy_timing_validate(&state->timing, state->period);
if (err < 0) {
dev_err(dsi->dev, "failed to validate D-PHY timing: %d\n", err);
return err;
}
/*
* We divide the frequency by two here, but we make up for that by
* setting the shift clock divider (further below) to half of the
* correct value.
*/
plld /= 2;
/*
* Derive pixel clock from bit clock using the shift clock divider.
* Note that this is only half of what we would expect, but we need
* that to make up for the fact that we divided the bit clock by a
* factor of two above.
*
* It's not clear exactly why this is necessary, but the display is
* not working properly otherwise. Perhaps the PLLs cannot generate
* frequencies sufficiently high.
*/
scdiv = ((8 * state->mul) / (state->div * state->lanes)) - 2;
err = tegra_dc_state_setup_clock(dc, crtc_state, dsi->clk_parent,
plld, scdiv);
if (err < 0) {
dev_err(output->dev, "failed to setup CRTC state: %d\n", err);
return err;
}
return err;
}
static const struct drm_encoder_helper_funcs tegra_dsi_encoder_helper_funcs = { static const struct drm_encoder_helper_funcs tegra_dsi_encoder_helper_funcs = {
.dpms = tegra_dsi_encoder_dpms, .dpms = tegra_dsi_encoder_dpms,
.mode_fixup = tegra_dsi_encoder_mode_fixup, .mode_fixup = tegra_dsi_encoder_mode_fixup,
...@@ -936,6 +1058,7 @@ static const struct drm_encoder_helper_funcs tegra_dsi_encoder_helper_funcs = { ...@@ -936,6 +1058,7 @@ static const struct drm_encoder_helper_funcs tegra_dsi_encoder_helper_funcs = {
.commit = tegra_dsi_encoder_commit, .commit = tegra_dsi_encoder_commit,
.mode_set = tegra_dsi_encoder_mode_set, .mode_set = tegra_dsi_encoder_mode_set,
.disable = tegra_dsi_encoder_disable, .disable = tegra_dsi_encoder_disable,
.atomic_check = tegra_dsi_encoder_atomic_check,
}; };
static int tegra_dsi_pad_enable(struct tegra_dsi *dsi) static int tegra_dsi_pad_enable(struct tegra_dsi *dsi)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment