drm/tegra: dsi: Demidlayer
authorThierry Reding <treding@nvidia.com>
Tue, 2 Dec 2014 16:30:23 +0000 (17:30 +0100)
committerThierry Reding <treding@nvidia.com>
Tue, 27 Jan 2015 09:14:48 +0000 (10:14 +0100)
Implement encoder and connector within the DSI driver itself using the
Tegra output helpers rather than using the Tegra output as midlayer. By
doing so one level of indirection is removed and output drivers become
more flexible while keeping the majority of the advantages provided by
the common output helpers.

Signed-off-by: Thierry Reding <treding@nvidia.com>
drivers/gpu/drm/tegra/drm.h
drivers/gpu/drm/tegra/dsi.c
drivers/gpu/drm/tegra/output.c

index 95b6aebfaf00b05d8c903c963495e6d614477756..ae3daa436ee6cd2c31079dc3662e63b02c42620b 100644 (file)
@@ -193,7 +193,6 @@ struct tegra_output_ops {
 };
 
 enum tegra_output_type {
-       TEGRA_OUTPUT_DSI,
        TEGRA_OUTPUT_EDP,
 };
 
index 4c0dfe92402a6e59a2945719508cf71b99f476f9..0b476e1c005a927491de22c78e9a904259b1ff79 100644 (file)
@@ -51,7 +51,6 @@ struct tegra_dsi {
        struct mipi_dsi_host host;
 
        struct regulator *vdd;
-       bool enabled;
 
        unsigned int video_fifo_depth;
        unsigned int host_fifo_depth;
@@ -628,46 +627,6 @@ static int tegra_dsi_configure(struct tegra_dsi *dsi, unsigned int pipe,
        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_commit(dc);
-
-       /* 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)
 {
        u32 value;
@@ -704,6 +663,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;
@@ -747,82 +729,51 @@ static void tegra_dsi_soft_reset(struct tegra_dsi *dsi)
                tegra_dsi_soft_reset(dsi->slave);
 }
 
-static int tegra_output_dsi_disable(struct tegra_output *output)
+static void tegra_dsi_connector_dpms(struct drm_connector *connector, int mode)
 {
-       struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc);
-       struct tegra_dsi *dsi = to_dsi(output);
-       u32 value;
-       int err;
-
-       if (!dsi->enabled)
-               return 0;
-
-       tegra_dsi_video_disable(dsi);
-
-       /*
-        * The following accesses registers of the display controller, so make
-        * 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_DISP_DISP_WIN_OPTIONS);
-               value &= ~DSI_ENABLE;
-               tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
-
-               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_soft_reset(dsi);
-       tegra_dsi_disable(dsi);
-
-       dsi->enabled = false;
-
-       return 0;
 }
 
-static void tegra_dsi_set_timeout(struct tegra_dsi *dsi, unsigned long bclk,
-                                 unsigned int vrefresh)
-{
-       unsigned int timeout;
-       u32 value;
+static const struct drm_connector_funcs tegra_dsi_connector_funcs = {
+       .dpms = tegra_dsi_connector_dpms,
+       .detect = tegra_output_connector_detect,
+       .fill_modes = drm_helper_probe_single_connector_modes,
+       .destroy = tegra_output_connector_destroy,
+};
 
-       /* 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);
+static enum drm_mode_status
+tegra_dsi_connector_mode_valid(struct drm_connector *connector,
+                              struct drm_display_mode *mode)
+{
+       return MODE_OK;
+}
 
-       /* 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);
+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,
+};
 
-       value = DSI_TALLY_TA(0) | DSI_TALLY_LRX(0) | DSI_TALLY_HTX(0);
-       tegra_dsi_writel(dsi, value, DSI_TO_TALLY);
+static const struct drm_encoder_funcs tegra_dsi_encoder_funcs = {
+       .destroy = tegra_output_encoder_destroy,
+};
 
-       if (dsi->slave)
-               tegra_dsi_set_timeout(dsi->slave, bclk, vrefresh);
+static void tegra_dsi_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
 }
 
-static int tegra_output_dsi_setup_clock(struct tegra_output *output,
-                                       struct clk *clk, unsigned long pclk,
-                                       unsigned int *divp)
+static bool tegra_dsi_encoder_mode_fixup(struct drm_encoder *encoder,
+                                        const struct drm_display_mode *mode,
+                                        struct drm_display_mode *adjusted)
 {
-       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_dc *dc = to_tegra_dc(encoder->crtc);
+       unsigned int mul, div, scdiv, vrefresh, lanes;
        struct tegra_dsi *dsi = to_dsi(output);
-       unsigned int mul, div, vrefresh, lanes;
-       unsigned long bclk, plld;
+       unsigned long pclk, bclk, plld;
        int err;
 
        lanes = tegra_dsi_get_lanes(dsi);
+       pclk = mode->clock * 1000;
 
        err = tegra_dsi_get_muldiv(dsi->format, &mul, &div);
        if (err < 0)
@@ -847,19 +798,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
@@ -870,39 +808,132 @@ 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 * mul) / (div * lanes)) - 2;
+
+       err = tegra_dc_setup_clock(dc, dsi->clk_parent, plld, scdiv);
+       if (err < 0) {
+               dev_err(output->dev, "failed to setup DC clock: %d\n", err);
+               return false;
+       }
+
+       err = clk_set_rate(dsi->clk_parent, plld);
+       if (err < 0) {
+               dev_err(dsi->dev, "failed to set clock rate to %lu Hz\n",
+                       plld);
+               return false;
+       }
 
-       /*
-        * 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)
-               return err;
+       if (err < 0) {
+               dev_err(dsi->dev, "failed to setup D-PHY timing: %d\n", err);
+               return false;
+       }
 
-       return 0;
+       return true;
+}
+
+static void tegra_dsi_encoder_prepare(struct drm_encoder *encoder)
+{
+}
+
+static void tegra_dsi_encoder_commit(struct drm_encoder *encoder)
+{
 }
 
-static int tegra_output_dsi_check_mode(struct tegra_output *output,
+static void tegra_dsi_encoder_mode_set(struct drm_encoder *encoder,
                                       struct drm_display_mode *mode,
-                                      enum drm_mode_status *status)
+                                      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);
+       u32 value;
+       int err;
+
+
+       err = tegra_dsi_configure(dsi, dc->pipe, mode);
+       if (err < 0) {
+               dev_err(dsi->dev, "failed to configure DSI: %d\n", err);
+               return;
+       }
+
+       if (output->panel)
+               drm_panel_prepare(output->panel);
+
+       /* 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_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);
+       u32 value;
+       int err;
+
+       if (output->panel)
+               drm_panel_disable(output->panel);
+
+       tegra_dsi_video_disable(dsi);
+
        /*
-        * FIXME: For now, always assume that the mode is okay.
+        * The following accesses registers of the display controller, so make
+        * sure it's only executed when the output is attached to one.
         */
+       if (dc) {
+               value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
+               value &= ~DSI_ENABLE;
+               tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
 
-       *status = MODE_OK;
+               tegra_dc_commit(dc);
+       }
 
-       return 0;
+       err = tegra_dsi_wait_idle(dsi, 100);
+       if (err < 0)
+               dev_dbg(dsi->dev, "failed to idle DSI: %d\n", err);
+
+       tegra_dsi_soft_reset(dsi);
+
+       if (output->panel)
+               drm_panel_unprepare(output->panel);
+
+       tegra_dsi_disable(dsi);
+
+       return;
 }
 
-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,
+       .mode_fixup = tegra_dsi_encoder_mode_fixup,
+       .prepare = tegra_dsi_encoder_prepare,
+       .commit = tegra_dsi_encoder_commit,
+       .mode_set = tegra_dsi_encoder_mode_set,
+       .disable = tegra_dsi_encoder_disable,
 };
 
 static int tegra_dsi_pad_enable(struct tegra_dsi *dsi)
@@ -952,15 +983,30 @@ static int tegra_dsi_init(struct host1x_client *client)
 
        /* 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;
 
-               err = tegra_output_init(drm, &dsi->output);
-               if (err < 0) {
-                       dev_err(client->dev, "output setup failed: %d\n", err);
-                       return err;
-               }
+               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;
+
+               if (dsi->output.panel)
+                       drm_panel_attach(dsi->output.panel,
+                                        &dsi->output.connector);
+
+               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);
+
+               dsi->output.encoder.possible_crtcs = 0x3;
        }
 
        if (IS_ENABLED(CONFIG_DEBUG_FS)) {
@@ -981,28 +1027,14 @@ static int tegra_dsi_exit(struct host1x_client *client)
        struct tegra_dsi *dsi = host1x_client_to_dsi(client);
        int err;
 
+       tegra_output_exit(&dsi->output);
+
        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);
        }
 
-       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;
-               }
-
-               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;
@@ -1547,6 +1579,12 @@ static int tegra_dsi_remove(struct platform_device *pdev)
                return err;
        }
 
+       err = tegra_output_remove(&dsi->output);
+       if (err < 0) {
+               dev_err(&pdev->dev, "failed to remove output: %d\n", err);
+               return err;
+       }
+
        mipi_dsi_host_unregister(&dsi->host);
        tegra_mipi_free(dsi->mipi);
 
@@ -1555,12 +1593,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;
 }
 
index c7ebe8ea38683c9589b26109f9e24249be40d725..81de885226ff87b1fcae16346769bf83f34e3e25 100644 (file)
@@ -274,11 +274,6 @@ int tegra_output_init(struct drm_device *drm, struct tegra_output *output)
        int connector, encoder;
 
        switch (output->type) {
-       case TEGRA_OUTPUT_DSI:
-               connector = DRM_MODE_CONNECTOR_DSI;
-               encoder = DRM_MODE_ENCODER_DSI;
-               break;
-
        case TEGRA_OUTPUT_EDP:
                connector = DRM_MODE_CONNECTOR_eDP;
                encoder = DRM_MODE_ENCODER_TMDS;