diff options
Diffstat (limited to 'drivers/gpu/drm/tegra/dsi.c')
-rw-r--r-- | drivers/gpu/drm/tegra/dsi.c | 578 |
1 files changed, 327 insertions, 251 deletions
diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c index 33f67fd..ed970f6 100644 --- a/drivers/gpu/drm/tegra/dsi.c +++ b/drivers/gpu/drm/tegra/dsi.c @@ -17,6 +17,7 @@ #include <linux/regulator/consumer.h> +#include <drm/drm_atomic_helper.h> #include <drm/drm_mipi_dsi.h> #include <drm/drm_panel.h> @@ -27,6 +28,28 @@ #include "dsi.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 host1x_client client; struct tegra_output output; @@ -51,7 +74,6 @@ struct tegra_dsi { struct mipi_dsi_host host; struct regulator *vdd; - bool enabled; unsigned int video_fifo_depth; unsigned int host_fifo_depth; @@ -77,13 +99,17 @@ static inline struct tegra_dsi *to_dsi(struct tegra_output *output) return container_of(output, struct tegra_dsi, output); } -static inline unsigned long tegra_dsi_readl(struct tegra_dsi *dsi, - unsigned long reg) +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) { return readl(dsi->regs + (reg << 2)); } -static inline void tegra_dsi_writel(struct tegra_dsi *dsi, unsigned long value, +static inline void tegra_dsi_writel(struct tegra_dsi *dsi, u32 value, unsigned long reg) { writel(value, dsi->regs + (reg << 2)); @@ -95,7 +121,7 @@ static int tegra_dsi_show_regs(struct seq_file *s, void *data) struct tegra_dsi *dsi = node->info_ent->data; #define DUMP_REG(name) \ - seq_printf(s, "%-32s %#05x %08lx\n", #name, name, \ + seq_printf(s, "%-32s %#05x %08x\n", #name, name, \ tegra_dsi_readl(dsi, name)) DUMP_REG(DSI_INCR_SYNCPT); @@ -230,7 +256,7 @@ remove: return err; } -static int tegra_dsi_debugfs_exit(struct tegra_dsi *dsi) +static void tegra_dsi_debugfs_exit(struct tegra_dsi *dsi) { drm_debugfs_remove_files(dsi->debugfs_files, ARRAY_SIZE(debugfs_files), dsi->minor); @@ -241,8 +267,6 @@ static int tegra_dsi_debugfs_exit(struct tegra_dsi *dsi) debugfs_remove(dsi->debugfs); dsi->debugfs = NULL; - - return 0; } #define PKT_ID0(id) ((((id) & 0x3f) << 3) | (1 << 9)) @@ -338,61 +362,36 @@ static const u32 pkt_seq_command_mode[NUM_PKT_SEQ] = { [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 value, period; - 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); - if (err < 0) - return err; - - 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; + u32 value; - 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); + 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); - value = DSI_TIMING_FIELD(timing.clktrail, period, 1) << 24 | - DSI_TIMING_FIELD(timing.clkpost, period, 1) << 16 | - DSI_TIMING_FIELD(timing.clkzero, period, 1) << 8 | - DSI_TIMING_FIELD(timing.lpx, period, 1); + value = DSI_TIMING_FIELD(timing->clktrail, period, 1) << 24 | + DSI_TIMING_FIELD(timing->clkpost, period, 1) << 16 | + DSI_TIMING_FIELD(timing->clkzero, period, 1) << 8 | + DSI_TIMING_FIELD(timing->lpx, period, 1); tegra_dsi_writel(dsi, value, DSI_PHY_TIMING_1); - value = DSI_TIMING_FIELD(timing.clkprepare, period, 1) << 16 | - DSI_TIMING_FIELD(timing.clkpre, period, 1) << 8 | + value = DSI_TIMING_FIELD(timing->clkprepare, period, 1) << 16 | + DSI_TIMING_FIELD(timing->clkpre, period, 1) << 8 | DSI_TIMING_FIELD(0xff * period, period, 0) << 0; tegra_dsi_writel(dsi, value, DSI_PHY_TIMING_2); - value = DSI_TIMING_FIELD(timing.taget, period, 1) << 16 | - DSI_TIMING_FIELD(timing.tasure, period, 1) << 8 | - DSI_TIMING_FIELD(timing.tago, period, 1); + value = DSI_TIMING_FIELD(timing->taget, period, 1) << 16 | + DSI_TIMING_FIELD(timing->tasure, period, 1) << 8 | + DSI_TIMING_FIELD(timing->tago, period, 1); tegra_dsi_writel(dsi, value, DSI_BTA_TIMING); if (dsi->slave) - return tegra_dsi_set_phy_timing(dsi->slave); - - return 0; + tegra_dsi_set_phy_timing(dsi->slave, period, timing); } static int tegra_dsi_get_muldiv(enum mipi_dsi_pixel_format format, @@ -484,14 +483,22 @@ static unsigned int tegra_dsi_get_lanes(struct tegra_dsi *dsi) return dsi->lanes; } -static int tegra_dsi_configure(struct tegra_dsi *dsi, unsigned int pipe, - const struct drm_display_mode *mode) +static void tegra_dsi_configure(struct tegra_dsi *dsi, unsigned int pipe, + const struct drm_display_mode *mode) { unsigned int hact, hsw, hbp, hfp, i, mul, div; - enum tegra_dsi_format format; + struct tegra_dsi_state *state; const u32 *pkt_seq; 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) { DRM_DEBUG_KMS("Non-burst video mode with sync pulses\n"); @@ -504,15 +511,8 @@ static int tegra_dsi_configure(struct tegra_dsi *dsi, unsigned int pipe, pkt_seq = pkt_seq_command_mode; } - err = tegra_dsi_get_muldiv(dsi->format, &mul, &div); - if (err < 0) - return err; - - err = tegra_dsi_get_format(dsi->format, &format); - if (err < 0) - return err; - - value = DSI_CONTROL_CHANNEL(0) | DSI_CONTROL_FORMAT(format) | + value = DSI_CONTROL_CHANNEL(0) | + DSI_CONTROL_FORMAT(state->format) | DSI_CONTROL_LANES(dsi->lanes - 1) | DSI_CONTROL_SOURCE(pipe); tegra_dsi_writel(dsi, value, DSI_CONTROL); @@ -591,8 +591,8 @@ static int tegra_dsi_configure(struct tegra_dsi *dsi, unsigned int pipe, /* set SOL delay */ if (dsi->master || dsi->slave) { - unsigned int lanes = tegra_dsi_get_lanes(dsi); unsigned long delay, bclk, bclk_ganged; + unsigned int lanes = state->lanes; /* SOL to valid, valid to FIFO and FIFO write delay */ delay = 4 + 4 + 2; @@ -612,9 +612,7 @@ static int tegra_dsi_configure(struct tegra_dsi *dsi, unsigned int pipe, } if (dsi->slave) { - err = tegra_dsi_configure(dsi->slave, pipe, mode); - if (err < 0) - return err; + tegra_dsi_configure(dsi->slave, pipe, mode); /* * TODO: Support modes other than symmetrical left-right @@ -624,49 +622,6 @@ static int tegra_dsi_configure(struct tegra_dsi *dsi, unsigned int pipe, 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; - tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); - - value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND); - value &= ~DISP_CTRL_MODE_MASK; - value |= DISP_CTRL_MODE_C_DISPLAY; - tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND); - - value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL); - value |= PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE | - PW4_ENABLE | PM0_ENABLE | PM1_ENABLE; - tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL); - - tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL); - tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); - - /* enable DSI controller */ - tegra_dsi_enable(dsi); - - dsi->enabled = true; - - return 0; } static int tegra_dsi_wait_idle(struct tegra_dsi *dsi, unsigned long timeout) @@ -705,6 +660,29 @@ static void tegra_dsi_ganged_disable(struct tegra_dsi *dsi) tegra_dsi_writel(dsi, 0, DSI_GANGED_MODE_CONTROL); } +static void tegra_dsi_set_timeout(struct tegra_dsi *dsi, unsigned long bclk, + unsigned int vrefresh) +{ + unsigned int timeout; + u32 value; + + /* 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); + + /* 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 void tegra_dsi_disable(struct tegra_dsi *dsi) { u32 value; @@ -724,15 +702,149 @@ static void tegra_dsi_disable(struct tegra_dsi *dsi) usleep_range(5000, 10000); } -static int tegra_output_dsi_disable(struct tegra_output *output) +static void tegra_dsi_soft_reset(struct tegra_dsi *dsi) +{ + u32 value; + + value = tegra_dsi_readl(dsi, DSI_POWER_CONTROL); + value &= ~DSI_POWER_CONTROL_ENABLE; + tegra_dsi_writel(dsi, value, DSI_POWER_CONTROL); + + usleep_range(300, 1000); + + value = tegra_dsi_readl(dsi, DSI_POWER_CONTROL); + value |= DSI_POWER_CONTROL_ENABLE; + tegra_dsi_writel(dsi, value, DSI_POWER_CONTROL); + + usleep_range(300, 1000); + + value = tegra_dsi_readl(dsi, DSI_TRIGGER); + if (value) + tegra_dsi_writel(dsi, 0, DSI_TRIGGER); + + if (dsi->slave) + tegra_dsi_soft_reset(dsi->slave); +} + +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 ©->base; +} + +static const struct drm_connector_funcs tegra_dsi_connector_funcs = { + .dpms = tegra_dsi_connector_dpms, + .reset = tegra_dsi_connector_reset, + .detect = tegra_output_connector_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = tegra_output_connector_destroy, + .atomic_duplicate_state = tegra_dsi_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, +}; + +static enum drm_mode_status +tegra_dsi_connector_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + return MODE_OK; +} + +static const struct drm_connector_helper_funcs tegra_dsi_connector_helper_funcs = { + .get_modes = tegra_output_connector_get_modes, + .mode_valid = tegra_dsi_connector_mode_valid, + .best_encoder = tegra_output_connector_best_encoder, +}; + +static const struct drm_encoder_funcs tegra_dsi_encoder_funcs = { + .destroy = tegra_output_encoder_destroy, +}; + +static void tegra_dsi_encoder_dpms(struct drm_encoder *encoder, int mode) +{ +} + +static void tegra_dsi_encoder_prepare(struct drm_encoder *encoder) +{ +} + +static void tegra_dsi_encoder_commit(struct drm_encoder *encoder) { - struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc); +} + +static void tegra_dsi_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted) +{ + struct tegra_output *output = encoder_to_output(encoder); + struct tegra_dc *dc = to_tegra_dc(encoder->crtc); + struct tegra_dsi *dsi = to_dsi(output); + struct tegra_dsi_state *state; + u32 value; + + state = tegra_dsi_get_state(dsi); + + tegra_dsi_set_timeout(dsi, state->bclk, state->vrefresh); + + /* + * 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) + drm_panel_prepare(output->panel); + + tegra_dsi_configure(dsi, dc->pipe, mode); + + /* enable display controller */ + value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); + value |= DSI_ENABLE; + tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); + + tegra_dc_commit(dc); + + /* enable DSI controller */ + tegra_dsi_enable(dsi); + + if (output->panel) + drm_panel_enable(output->panel); + + return; +} + +static void tegra_dsi_encoder_disable(struct drm_encoder *encoder) +{ + struct tegra_output *output = encoder_to_output(encoder); + struct tegra_dc *dc = to_tegra_dc(encoder->crtc); struct tegra_dsi *dsi = to_dsi(output); - unsigned long value; + u32 value; int err; - if (!dsi->enabled) - return 0; + if (output->panel) + drm_panel_disable(output->panel); tegra_dsi_video_disable(dsi); @@ -741,85 +853,78 @@ static int tegra_output_dsi_disable(struct tegra_output *output) * sure it's only executed when the output is attached to one. */ if (dc) { - value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL); - value &= ~(PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE | - PW4_ENABLE | PM0_ENABLE | PM1_ENABLE); - tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL); - - value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND); - value &= ~DISP_CTRL_MODE_MASK; - tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND); - value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); value &= ~DSI_ENABLE; tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); - tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL); - tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); + tegra_dc_commit(dc); } err = tegra_dsi_wait_idle(dsi, 100); if (err < 0) dev_dbg(dsi->dev, "failed to idle DSI: %d\n", err); - tegra_dsi_disable(dsi); + tegra_dsi_soft_reset(dsi); - dsi->enabled = false; + if (output->panel) + drm_panel_unprepare(output->panel); - return 0; -} - -static void tegra_dsi_set_timeout(struct tegra_dsi *dsi, unsigned long bclk, - unsigned int vrefresh) -{ - unsigned int timeout; - u32 value; - - /* 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); - - /* 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); + tegra_dsi_disable(dsi); - if (dsi->slave) - tegra_dsi_set_timeout(dsi->slave, bclk, vrefresh); + return; } -static int tegra_output_dsi_setup_clock(struct tegra_output *output, - struct clk *clk, unsigned long pclk, - unsigned int *divp) +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_dc *dc = to_tegra_dc(output->encoder.crtc); - struct drm_display_mode *mode = &dc->base.mode; + 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 mul, div, vrefresh, lanes; - unsigned long bclk, plld; + unsigned int scdiv; + unsigned long plld; int err; - lanes = tegra_dsi_get_lanes(dsi); + 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_muldiv(dsi->format, &mul, &div); + err = tegra_dsi_get_format(dsi->format, &state->format); if (err < 0) return err; - DRM_DEBUG_KMS("mul: %u, div: %u, lanes: %u\n", mul, div, lanes); - vrefresh = drm_mode_vrefresh(mode); - DRM_DEBUG_KMS("vrefresh: %u\n", vrefresh); + state->vrefresh = drm_mode_vrefresh(&crtc_state->mode); /* compute byte clock */ - bclk = (pclk * mul) / (div * lanes); + 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(bclk * 8, USEC_PER_SEC) * USEC_PER_SEC; + 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 @@ -828,19 +933,6 @@ static int tegra_output_dsi_setup_clock(struct tegra_output *output, */ plld /= 2; - err = clk_set_parent(clk, dsi->clk_parent); - if (err < 0) { - dev_err(dsi->dev, "failed to set parent clock: %d\n", err); - return err; - } - - err = clk_set_rate(dsi->clk_parent, plld); - if (err < 0) { - dev_err(dsi->dev, "failed to set base clock rate to %lu Hz\n", - plld); - return err; - } - /* * 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 @@ -851,44 +943,30 @@ static int tegra_output_dsi_setup_clock(struct tegra_output *output, * not working properly otherwise. Perhaps the PLLs cannot generate * frequencies sufficiently high. */ - *divp = ((8 * mul) / (div * lanes)) - 2; + scdiv = ((8 * state->mul) / (state->div * state->lanes)) - 2; - /* - * XXX: Move the below somewhere else so that we don't need to have - * access to the vrefresh in this function? - */ - tegra_dsi_set_timeout(dsi, bclk, vrefresh); - - err = tegra_dsi_set_phy_timing(dsi); - if (err < 0) + 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 0; -} - -static int tegra_output_dsi_check_mode(struct tegra_output *output, - struct drm_display_mode *mode, - enum drm_mode_status *status) -{ - /* - * FIXME: For now, always assume that the mode is okay. - */ - - *status = MODE_OK; - - return 0; + return err; } -static const struct tegra_output_ops dsi_ops = { - .enable = tegra_output_dsi_enable, - .disable = tegra_output_dsi_disable, - .setup_clock = tegra_output_dsi_setup_clock, - .check_mode = tegra_output_dsi_check_mode, +static const struct drm_encoder_helper_funcs tegra_dsi_encoder_helper_funcs = { + .dpms = tegra_dsi_encoder_dpms, + .prepare = tegra_dsi_encoder_prepare, + .commit = tegra_dsi_encoder_commit, + .mode_set = tegra_dsi_encoder_mode_set, + .disable = tegra_dsi_encoder_disable, + .atomic_check = tegra_dsi_encoder_atomic_check, }; static int tegra_dsi_pad_enable(struct tegra_dsi *dsi) { - unsigned long value; + u32 value; value = DSI_PAD_CONTROL_VS1_PULLDN(0) | DSI_PAD_CONTROL_VS1_PDIO(0); tegra_dsi_writel(dsi, value, DSI_PAD_CONTROL_0); @@ -923,17 +1001,44 @@ static int tegra_dsi_init(struct host1x_client *client) struct tegra_dsi *dsi = host1x_client_to_dsi(client); int err; + reset_control_deassert(dsi->rst); + + err = tegra_dsi_pad_calibrate(dsi); + if (err < 0) { + dev_err(dsi->dev, "MIPI calibration failed: %d\n", err); + goto reset; + } + /* Gangsters must not register their own outputs. */ if (!dsi->master) { - dsi->output.type = TEGRA_OUTPUT_DSI; dsi->output.dev = client->dev; - dsi->output.ops = &dsi_ops; + + drm_connector_init(drm, &dsi->output.connector, + &tegra_dsi_connector_funcs, + DRM_MODE_CONNECTOR_DSI); + drm_connector_helper_add(&dsi->output.connector, + &tegra_dsi_connector_helper_funcs); + dsi->output.connector.dpms = DRM_MODE_DPMS_OFF; + + drm_encoder_init(drm, &dsi->output.encoder, + &tegra_dsi_encoder_funcs, + DRM_MODE_ENCODER_DSI); + drm_encoder_helper_add(&dsi->output.encoder, + &tegra_dsi_encoder_helper_funcs); + + drm_mode_connector_attach_encoder(&dsi->output.connector, + &dsi->output.encoder); + drm_connector_register(&dsi->output.connector); err = tegra_output_init(drm, &dsi->output); if (err < 0) { - dev_err(client->dev, "output setup failed: %d\n", err); - return err; + dev_err(client->dev, + "failed to initialize output: %d\n", + err); + goto reset; } + + dsi->output.encoder.possible_crtcs = 0x3; } if (IS_ENABLED(CONFIG_DEBUG_FS)) { @@ -943,34 +1048,22 @@ static int tegra_dsi_init(struct host1x_client *client) } return 0; + +reset: + reset_control_assert(dsi->rst); + return err; } static int tegra_dsi_exit(struct host1x_client *client) { struct tegra_dsi *dsi = host1x_client_to_dsi(client); - int err; - if (IS_ENABLED(CONFIG_DEBUG_FS)) { - err = tegra_dsi_debugfs_exit(dsi); - if (err < 0) - dev_err(dsi->dev, "debugfs cleanup failed: %d\n", err); - } + tegra_output_exit(&dsi->output); - if (!dsi->master) { - err = tegra_output_disable(&dsi->output); - if (err < 0) { - dev_err(client->dev, "output failed to disable: %d\n", - err); - return err; - } + if (IS_ENABLED(CONFIG_DEBUG_FS)) + tegra_dsi_debugfs_exit(dsi); - err = tegra_output_exit(&dsi->output); - if (err < 0) { - dev_err(client->dev, "output cleanup failed: %d\n", - err); - return err; - } - } + reset_control_assert(dsi->rst); return 0; } @@ -1398,13 +1491,6 @@ static int tegra_dsi_probe(struct platform_device *pdev) if (IS_ERR(dsi->rst)) return PTR_ERR(dsi->rst); - err = reset_control_deassert(dsi->rst); - if (err < 0) { - dev_err(&pdev->dev, "failed to bring DSI out of reset: %d\n", - err); - return err; - } - dsi->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(dsi->clk)) { dev_err(&pdev->dev, "cannot get DSI clock\n"); @@ -1470,12 +1556,6 @@ static int tegra_dsi_probe(struct platform_device *pdev) goto disable_vdd; } - err = tegra_dsi_pad_calibrate(dsi); - if (err < 0) { - dev_err(dsi->dev, "MIPI calibration failed: %d\n", err); - goto mipi_free; - } - dsi->host.ops = &tegra_dsi_host_ops; dsi->host.dev = &pdev->dev; @@ -1527,6 +1607,8 @@ static int tegra_dsi_remove(struct platform_device *pdev) return err; } + tegra_output_remove(&dsi->output); + mipi_dsi_host_unregister(&dsi->host); tegra_mipi_free(dsi->mipi); @@ -1535,12 +1617,6 @@ static int tegra_dsi_remove(struct platform_device *pdev) clk_disable_unprepare(dsi->clk); reset_control_assert(dsi->rst); - err = tegra_output_remove(&dsi->output); - if (err < 0) { - dev_err(&pdev->dev, "failed to remove output: %d\n", err); - return err; - } - return 0; } |