drm/msm/mdp5: Enable DSI connector in msm drm driver
authorHai Li <hali@codeaurora.org>
Thu, 26 Mar 2015 23:25:17 +0000 (19:25 -0400)
committerRob Clark <robdclark@gmail.com>
Wed, 1 Apr 2015 23:29:38 +0000 (19:29 -0400)
This change adds the support in mdp5 kms driver for single
and dual DSI. Dual DSI case depends on the framework API
and sequence change to support dual data path.

v1: Initial change
v2: Address Rob Clark's comment
- Separate command mode encoder to a new file mdp5_cmd_encoder.c
- Rebase to not depend on msm_drm_sub_dev change

Signed-off-by: Hai Li <hali@codeaurora.org>
Signed-off-by: Rob Clark <robdclark@gmail.com>
drivers/gpu/drm/msm/Makefile
drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.c
drivers/gpu/drm/msm/mdp/mdp5/mdp5_cmd_encoder.c [new file with mode: 0644]
drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c
drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c
drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h
drivers/gpu/drm/msm/msm_drv.c

index 5c144cc5f8db314f6cbcb502290a876d03efb1c1..ab2086783feec509fa982c477cd8de504447ae72 100644 (file)
@@ -53,6 +53,7 @@ msm-$(CONFIG_COMMON_CLK) += mdp/mdp4/mdp4_lvds_pll.o
 msm-$(CONFIG_DRM_MSM_DSI) += dsi/dsi.o \
                        dsi/dsi_host.o \
                        dsi/dsi_manager.o \
-                       dsi/dsi_phy.o
+                       dsi/dsi_phy.o \
+                       mdp/mdp5/mdp5_cmd_encoder.o
 
 obj-$(CONFIG_DRM_MSM)  += msm.o
index 56dfc5b626dd78ece90a1d2a8ec1973cda6fe4e1..e001e6b2296a2ebd5cf602fbf0e3ba89f49ecf0d 100644 (file)
@@ -77,6 +77,8 @@ const struct mdp5_cfg_hw msm8x74_config = {
        },
        .intfs = {
                [0] = INTF_eDP,
+               [1] = INTF_DSI,
+               [2] = INTF_DSI,
                [3] = INTF_HDMI,
        },
        .max_clk = 200000000,
@@ -145,6 +147,8 @@ const struct mdp5_cfg_hw apq8084_config = {
        },
        .intfs = {
                [0] = INTF_eDP,
+               [1] = INTF_DSI,
+               [2] = INTF_DSI,
                [3] = INTF_HDMI,
        },
        .max_clk = 320000000,
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cmd_encoder.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cmd_encoder.c
new file mode 100644 (file)
index 0000000..e4e8956
--- /dev/null
@@ -0,0 +1,343 @@
+/*
+ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include "mdp5_kms.h"
+
+#include "drm_crtc.h"
+#include "drm_crtc_helper.h"
+
+struct mdp5_cmd_encoder {
+       struct drm_encoder base;
+       struct mdp5_interface intf;
+       bool enabled;
+       uint32_t bsc;
+};
+#define to_mdp5_cmd_encoder(x) container_of(x, struct mdp5_cmd_encoder, base)
+
+static struct mdp5_kms *get_kms(struct drm_encoder *encoder)
+{
+       struct msm_drm_private *priv = encoder->dev->dev_private;
+       return to_mdp5_kms(to_mdp_kms(priv->kms));
+}
+
+#ifdef CONFIG_MSM_BUS_SCALING
+#include <mach/board.h>
+#include <linux/msm-bus.h>
+#include <linux/msm-bus-board.h>
+#define MDP_BUS_VECTOR_ENTRY(ab_val, ib_val)           \
+       {                                               \
+               .src = MSM_BUS_MASTER_MDP_PORT0,        \
+               .dst = MSM_BUS_SLAVE_EBI_CH0,           \
+               .ab = (ab_val),                         \
+               .ib = (ib_val),                         \
+       }
+
+static struct msm_bus_vectors mdp_bus_vectors[] = {
+       MDP_BUS_VECTOR_ENTRY(0, 0),
+       MDP_BUS_VECTOR_ENTRY(2000000000, 2000000000),
+};
+static struct msm_bus_paths mdp_bus_usecases[] = { {
+               .num_paths = 1,
+               .vectors = &mdp_bus_vectors[0],
+}, {
+               .num_paths = 1,
+               .vectors = &mdp_bus_vectors[1],
+} };
+static struct msm_bus_scale_pdata mdp_bus_scale_table = {
+       .usecase = mdp_bus_usecases,
+       .num_usecases = ARRAY_SIZE(mdp_bus_usecases),
+       .name = "mdss_mdp",
+};
+
+static void bs_init(struct mdp5_cmd_encoder *mdp5_cmd_enc)
+{
+       mdp5_cmd_enc->bsc = msm_bus_scale_register_client(
+                       &mdp_bus_scale_table);
+       DBG("bus scale client: %08x", mdp5_cmd_enc->bsc);
+}
+
+static void bs_fini(struct mdp5_cmd_encoder *mdp5_cmd_enc)
+{
+       if (mdp5_cmd_enc->bsc) {
+               msm_bus_scale_unregister_client(mdp5_cmd_enc->bsc);
+               mdp5_cmd_enc->bsc = 0;
+       }
+}
+
+static void bs_set(struct mdp5_cmd_encoder *mdp5_cmd_enc, int idx)
+{
+       if (mdp5_cmd_enc->bsc) {
+               DBG("set bus scaling: %d", idx);
+               /* HACK: scaling down, and then immediately back up
+                * seems to leave things broken (underflow).. so
+                * never disable:
+                */
+               idx = 1;
+               msm_bus_scale_client_update_request(mdp5_cmd_enc->bsc, idx);
+       }
+}
+#else
+static void bs_init(struct mdp5_cmd_encoder *mdp5_cmd_enc) {}
+static void bs_fini(struct mdp5_cmd_encoder *mdp5_cmd_enc) {}
+static void bs_set(struct mdp5_cmd_encoder *mdp5_cmd_enc, int idx) {}
+#endif
+
+#define VSYNC_CLK_RATE 19200000
+static int pingpong_tearcheck_setup(struct drm_encoder *encoder,
+                                       struct drm_display_mode *mode)
+{
+       struct mdp5_kms *mdp5_kms = get_kms(encoder);
+       struct device *dev = encoder->dev->dev;
+       u32 total_lines_x100, vclks_line, cfg;
+       long vsync_clk_speed;
+       int pp_id = GET_PING_PONG_ID(mdp5_crtc_get_lm(encoder->crtc));
+
+       if (IS_ERR_OR_NULL(mdp5_kms->vsync_clk)) {
+               dev_err(dev, "vsync_clk is not initialized\n");
+               return -EINVAL;
+       }
+
+       total_lines_x100 = mode->vtotal * mode->vrefresh;
+       if (!total_lines_x100) {
+               dev_err(dev, "%s: vtotal(%d) or vrefresh(%d) is 0\n",
+                               __func__, mode->vtotal, mode->vrefresh);
+               return -EINVAL;
+       }
+
+       vsync_clk_speed = clk_round_rate(mdp5_kms->vsync_clk, VSYNC_CLK_RATE);
+       if (vsync_clk_speed <= 0) {
+               dev_err(dev, "vsync_clk round rate failed %ld\n",
+                                                       vsync_clk_speed);
+               return -EINVAL;
+       }
+       vclks_line = vsync_clk_speed * 100 / total_lines_x100;
+
+       cfg = MDP5_PP_SYNC_CONFIG_VSYNC_COUNTER_EN
+               | MDP5_PP_SYNC_CONFIG_VSYNC_IN_EN;
+       cfg |= MDP5_PP_SYNC_CONFIG_VSYNC_COUNT(vclks_line);
+
+       mdp5_write(mdp5_kms, REG_MDP5_PP_SYNC_CONFIG_VSYNC(pp_id), cfg);
+       mdp5_write(mdp5_kms,
+               REG_MDP5_PP_SYNC_CONFIG_HEIGHT(pp_id), 0xfff0);
+       mdp5_write(mdp5_kms,
+               REG_MDP5_PP_VSYNC_INIT_VAL(pp_id), mode->vdisplay);
+       mdp5_write(mdp5_kms, REG_MDP5_PP_RD_PTR_IRQ(pp_id), mode->vdisplay + 1);
+       mdp5_write(mdp5_kms, REG_MDP5_PP_START_POS(pp_id), mode->vdisplay);
+       mdp5_write(mdp5_kms, REG_MDP5_PP_SYNC_THRESH(pp_id),
+                       MDP5_PP_SYNC_THRESH_START(4) |
+                       MDP5_PP_SYNC_THRESH_CONTINUE(4));
+
+       return 0;
+}
+
+static int pingpong_tearcheck_enable(struct drm_encoder *encoder)
+{
+       struct mdp5_kms *mdp5_kms = get_kms(encoder);
+       int pp_id = GET_PING_PONG_ID(mdp5_crtc_get_lm(encoder->crtc));
+       int ret;
+
+       ret = clk_set_rate(mdp5_kms->vsync_clk,
+               clk_round_rate(mdp5_kms->vsync_clk, VSYNC_CLK_RATE));
+       if (ret) {
+               dev_err(encoder->dev->dev,
+                       "vsync_clk clk_set_rate failed, %d\n", ret);
+               return ret;
+       }
+       ret = clk_prepare_enable(mdp5_kms->vsync_clk);
+       if (ret) {
+               dev_err(encoder->dev->dev,
+                       "vsync_clk clk_prepare_enable failed, %d\n", ret);
+               return ret;
+       }
+
+       mdp5_write(mdp5_kms, REG_MDP5_PP_TEAR_CHECK_EN(pp_id), 1);
+
+       return 0;
+}
+
+static void pingpong_tearcheck_disable(struct drm_encoder *encoder)
+{
+       struct mdp5_kms *mdp5_kms = get_kms(encoder);
+       int pp_id = GET_PING_PONG_ID(mdp5_crtc_get_lm(encoder->crtc));
+
+       mdp5_write(mdp5_kms, REG_MDP5_PP_TEAR_CHECK_EN(pp_id), 0);
+       clk_disable_unprepare(mdp5_kms->vsync_clk);
+}
+
+static void mdp5_cmd_encoder_destroy(struct drm_encoder *encoder)
+{
+       struct mdp5_cmd_encoder *mdp5_cmd_enc = to_mdp5_cmd_encoder(encoder);
+       bs_fini(mdp5_cmd_enc);
+       drm_encoder_cleanup(encoder);
+       kfree(mdp5_cmd_enc);
+}
+
+static const struct drm_encoder_funcs mdp5_cmd_encoder_funcs = {
+       .destroy = mdp5_cmd_encoder_destroy,
+};
+
+static bool mdp5_cmd_encoder_mode_fixup(struct drm_encoder *encoder,
+               const struct drm_display_mode *mode,
+               struct drm_display_mode *adjusted_mode)
+{
+       return true;
+}
+
+static void mdp5_cmd_encoder_mode_set(struct drm_encoder *encoder,
+               struct drm_display_mode *mode,
+               struct drm_display_mode *adjusted_mode)
+{
+       struct mdp5_cmd_encoder *mdp5_cmd_enc = to_mdp5_cmd_encoder(encoder);
+
+       mode = adjusted_mode;
+
+       DBG("set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x",
+                       mode->base.id, mode->name,
+                       mode->vrefresh, mode->clock,
+                       mode->hdisplay, mode->hsync_start,
+                       mode->hsync_end, mode->htotal,
+                       mode->vdisplay, mode->vsync_start,
+                       mode->vsync_end, mode->vtotal,
+                       mode->type, mode->flags);
+       pingpong_tearcheck_setup(encoder, mode);
+       mdp5_crtc_set_intf(encoder->crtc, &mdp5_cmd_enc->intf);
+}
+
+static void mdp5_cmd_encoder_disable(struct drm_encoder *encoder)
+{
+       struct mdp5_cmd_encoder *mdp5_cmd_enc = to_mdp5_cmd_encoder(encoder);
+       struct mdp5_kms *mdp5_kms = get_kms(encoder);
+       struct mdp5_ctl *ctl = mdp5_crtc_get_ctl(encoder->crtc);
+       struct mdp5_interface *intf = &mdp5_cmd_enc->intf;
+       int lm = mdp5_crtc_get_lm(encoder->crtc);
+
+       if (WARN_ON(!mdp5_cmd_enc->enabled))
+               return;
+
+       /* Wait for the last frame done */
+       mdp_irq_wait(&mdp5_kms->base, lm2ppdone(lm));
+       pingpong_tearcheck_disable(encoder);
+
+       mdp5_ctl_set_encoder_state(ctl, false);
+       mdp5_ctl_commit(ctl, mdp_ctl_flush_mask_encoder(intf));
+
+       bs_set(mdp5_cmd_enc, 0);
+
+       mdp5_cmd_enc->enabled = false;
+}
+
+static void mdp5_cmd_encoder_enable(struct drm_encoder *encoder)
+{
+       struct mdp5_cmd_encoder *mdp5_cmd_enc = to_mdp5_cmd_encoder(encoder);
+       struct mdp5_ctl *ctl = mdp5_crtc_get_ctl(encoder->crtc);
+       struct mdp5_interface *intf = &mdp5_cmd_enc->intf;
+
+       if (WARN_ON(mdp5_cmd_enc->enabled))
+               return;
+
+       bs_set(mdp5_cmd_enc, 1);
+       if (pingpong_tearcheck_enable(encoder))
+               return;
+
+       mdp5_ctl_commit(ctl, mdp_ctl_flush_mask_encoder(intf));
+
+       mdp5_ctl_set_encoder_state(ctl, true);
+
+       mdp5_cmd_enc->enabled = true;
+}
+
+static const struct drm_encoder_helper_funcs mdp5_cmd_encoder_helper_funcs = {
+       .mode_fixup = mdp5_cmd_encoder_mode_fixup,
+       .mode_set = mdp5_cmd_encoder_mode_set,
+       .disable = mdp5_cmd_encoder_disable,
+       .enable = mdp5_cmd_encoder_enable,
+};
+
+int mdp5_cmd_encoder_set_split_display(struct drm_encoder *encoder,
+                                       struct drm_encoder *slave_encoder)
+{
+       struct mdp5_cmd_encoder *mdp5_cmd_enc = to_mdp5_cmd_encoder(encoder);
+       struct mdp5_kms *mdp5_kms;
+       int intf_num;
+       u32 data = 0;
+
+       if (!encoder || !slave_encoder)
+               return -EINVAL;
+
+       mdp5_kms = get_kms(encoder);
+       intf_num = mdp5_cmd_enc->intf.num;
+
+       /* Switch slave encoder's trigger MUX, to use the master's
+        * start signal for the slave encoder
+        */
+       if (intf_num == 1)
+               data |= MDP5_SPLIT_DPL_UPPER_INTF2_SW_TRG_MUX;
+       else if (intf_num == 2)
+               data |= MDP5_SPLIT_DPL_UPPER_INTF1_SW_TRG_MUX;
+       else
+               return -EINVAL;
+
+       /* Smart Panel, Sync mode */
+       data |= MDP5_SPLIT_DPL_UPPER_SMART_PANEL;
+
+       /* Make sure clocks are on when connectors calling this function. */
+       mdp5_enable(mdp5_kms);
+       mdp5_write(mdp5_kms, REG_MDP5_SPLIT_DPL_UPPER, data);
+
+       mdp5_write(mdp5_kms, REG_MDP5_SPLIT_DPL_LOWER,
+                       MDP5_SPLIT_DPL_LOWER_SMART_PANEL);
+       mdp5_write(mdp5_kms, REG_MDP5_SPLIT_DPL_EN, 1);
+       mdp5_disable(mdp5_kms);
+
+       return 0;
+}
+
+/* initialize command mode encoder */
+struct drm_encoder *mdp5_cmd_encoder_init(struct drm_device *dev,
+                               struct mdp5_interface *intf)
+{
+       struct drm_encoder *encoder = NULL;
+       struct mdp5_cmd_encoder *mdp5_cmd_enc;
+       int ret;
+
+       if (WARN_ON((intf->type != INTF_DSI) &&
+               (intf->mode != MDP5_INTF_DSI_MODE_COMMAND))) {
+               ret = -EINVAL;
+               goto fail;
+       }
+
+       mdp5_cmd_enc = kzalloc(sizeof(*mdp5_cmd_enc), GFP_KERNEL);
+       if (!mdp5_cmd_enc) {
+               ret = -ENOMEM;
+               goto fail;
+       }
+
+       memcpy(&mdp5_cmd_enc->intf, intf, sizeof(mdp5_cmd_enc->intf));
+       encoder = &mdp5_cmd_enc->base;
+
+       drm_encoder_init(dev, encoder, &mdp5_cmd_encoder_funcs,
+                       DRM_MODE_ENCODER_DSI);
+
+       drm_encoder_helper_add(encoder, &mdp5_cmd_encoder_helper_funcs);
+
+       bs_init(mdp5_cmd_enc);
+
+       return encoder;
+
+fail:
+       if (encoder)
+               mdp5_cmd_encoder_destroy(encoder);
+
+       return ERR_PTR(ret);
+}
+
index 9527ad112446c6e80da0ba6757288c5d0e6b020b..c1530772187dbd36bf672723af074f0af9a44c5d 100644 (file)
@@ -622,7 +622,16 @@ void mdp5_crtc_set_intf(struct drm_crtc *crtc, struct mdp5_interface *intf)
 
        /* now that we know what irq's we want: */
        mdp5_crtc->err.irqmask = intf2err(intf->num);
-       mdp5_crtc->vblank.irqmask = intf2vblank(lm, intf);
+
+       /* Register command mode Pingpong done as vblank for now,
+        * so that atomic commit should wait for it to finish.
+        * Ideally, in the future, we should take rd_ptr done as vblank,
+        * and let atomic commit wait for pingpong done for commond mode.
+        */
+       if (intf->mode == MDP5_INTF_DSI_MODE_COMMAND)
+               mdp5_crtc->vblank.irqmask = lm2ppdone(lm);
+       else
+               mdp5_crtc->vblank.irqmask = intf2vblank(lm, intf);
        mdp_irq_update(&mdp5_kms->base);
 
        mdp5_ctl_set_intf(mdp5_crtc->ctl, intf);
index 2ef6d1b0a218c532a27e67288eb92787b7db8bf8..1188f4bf1e603853e1d872dd0ad06ee55de9cceb 100644 (file)
@@ -286,12 +286,51 @@ static const struct drm_encoder_helper_funcs mdp5_encoder_helper_funcs = {
        .enable = mdp5_encoder_enable,
 };
 
+int mdp5_encoder_set_split_display(struct drm_encoder *encoder,
+                                       struct drm_encoder *slave_encoder)
+{
+       struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder);
+       struct mdp5_kms *mdp5_kms;
+       int intf_num;
+       u32 data = 0;
+
+       if (!encoder || !slave_encoder)
+               return -EINVAL;
+
+       mdp5_kms = get_kms(encoder);
+       intf_num = mdp5_encoder->intf.num;
+
+       /* Switch slave encoder's TimingGen Sync mode,
+        * to use the master's enable signal for the slave encoder.
+        */
+       if (intf_num == 1)
+               data |= MDP5_SPLIT_DPL_LOWER_INTF2_TG_SYNC;
+       else if (intf_num == 2)
+               data |= MDP5_SPLIT_DPL_LOWER_INTF1_TG_SYNC;
+       else
+               return -EINVAL;
+
+       /* Make sure clocks are on when connectors calling this function. */
+       mdp5_enable(mdp5_kms);
+       mdp5_write(mdp5_kms, REG_MDP5_MDP_SPARE_0(0),
+               MDP5_MDP_SPARE_0_SPLIT_DPL_SINGLE_FLUSH_EN);
+       /* Dumb Panel, Sync mode */
+       mdp5_write(mdp5_kms, REG_MDP5_SPLIT_DPL_UPPER, 0);
+       mdp5_write(mdp5_kms, REG_MDP5_SPLIT_DPL_LOWER, data);
+       mdp5_write(mdp5_kms, REG_MDP5_SPLIT_DPL_EN, 1);
+       mdp5_disable(mdp5_kms);
+
+       return 0;
+}
+
 /* initialize encoder */
 struct drm_encoder *mdp5_encoder_init(struct drm_device *dev,
                                struct mdp5_interface *intf)
 {
        struct drm_encoder *encoder = NULL;
        struct mdp5_encoder *mdp5_encoder;
+       int enc_type = (intf->type == INTF_DSI) ?
+               DRM_MODE_ENCODER_DSI : DRM_MODE_ENCODER_TMDS;
        int ret;
 
        mdp5_encoder = kzalloc(sizeof(*mdp5_encoder), GFP_KERNEL);
@@ -305,8 +344,8 @@ struct drm_encoder *mdp5_encoder_init(struct drm_device *dev,
 
        spin_lock_init(&mdp5_encoder->intf_lock);
 
-       drm_encoder_init(dev, encoder, &mdp5_encoder_funcs,
-                        DRM_MODE_ENCODER_TMDS);
+       drm_encoder_init(dev, encoder, &mdp5_encoder_funcs, enc_type);
+
        drm_encoder_helper_add(encoder, &mdp5_encoder_helper_funcs);
 
        bs_init(mdp5_encoder);
index 4d8f5b471e4d3b1c6ab4262011ea90ce7db602ad..dfa8beb9343aaa171110adabb49c549e4143c0da 100644 (file)
@@ -86,6 +86,18 @@ static long mdp5_round_pixclk(struct msm_kms *kms, unsigned long rate,
        return rate;
 }
 
+static int mdp5_set_split_display(struct msm_kms *kms,
+               struct drm_encoder *encoder,
+               struct drm_encoder *slave_encoder,
+               bool is_cmd_mode)
+{
+       if (is_cmd_mode)
+               return mdp5_cmd_encoder_set_split_display(encoder,
+                                                       slave_encoder);
+       else
+               return mdp5_encoder_set_split_display(encoder, slave_encoder);
+}
+
 static void mdp5_preclose(struct msm_kms *kms, struct drm_file *file)
 {
        struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms));
@@ -131,6 +143,7 @@ static const struct mdp_kms_funcs kms_funcs = {
                .complete_commit = mdp5_complete_commit,
                .get_format      = mdp_get_format,
                .round_pixclk    = mdp5_round_pixclk,
+               .set_split_display = mdp5_set_split_display,
                .preclose        = mdp5_preclose,
                .destroy         = mdp5_destroy,
        },
@@ -174,7 +187,12 @@ static struct drm_encoder *construct_encoder(struct mdp5_kms *mdp5_kms,
                        .mode   = intf_mode,
        };
 
-       encoder = mdp5_encoder_init(dev, &intf);
+       if ((intf_type == INTF_DSI) &&
+               (intf_mode == MDP5_INTF_DSI_MODE_COMMAND))
+               encoder = mdp5_cmd_encoder_init(dev, &intf);
+       else
+               encoder = mdp5_encoder_init(dev, &intf);
+
        if (IS_ERR(encoder)) {
                dev_err(dev->dev, "failed to construct encoder\n");
                return encoder;
@@ -186,6 +204,24 @@ static struct drm_encoder *construct_encoder(struct mdp5_kms *mdp5_kms,
        return encoder;
 }
 
+static int get_dsi_id_from_intf(const struct mdp5_cfg_hw *hw_cfg, int intf_num)
+{
+       const int intf_cnt = hw_cfg->intf.count;
+       const u32 *intfs = hw_cfg->intfs;
+       int id = 0, i;
+
+       for (i = 0; i < intf_cnt; i++) {
+               if (intfs[i] == INTF_DSI) {
+                       if (intf_num == i)
+                               return id;
+
+                       id++;
+               }
+       }
+
+       return -EINVAL;
+}
+
 static int modeset_init_intf(struct mdp5_kms *mdp5_kms, int intf_num)
 {
        struct drm_device *dev = mdp5_kms->dev;
@@ -225,6 +261,38 @@ static int modeset_init_intf(struct mdp5_kms *mdp5_kms, int intf_num)
 
                ret = hdmi_modeset_init(priv->hdmi, dev, encoder);
                break;
+       case INTF_DSI:
+       {
+               int dsi_id = get_dsi_id_from_intf(hw_cfg, intf_num);
+               struct drm_encoder *dsi_encs[MSM_DSI_ENCODER_NUM];
+               enum mdp5_intf_mode mode;
+               int i;
+
+               if ((dsi_id >= ARRAY_SIZE(priv->dsi)) || (dsi_id < 0)) {
+                       dev_err(dev->dev, "failed to find dsi from intf %d\n",
+                               intf_num);
+                       ret = -EINVAL;
+                       break;
+               }
+
+               if (!priv->dsi[dsi_id])
+                       break;
+
+               for (i = 0; i < MSM_DSI_ENCODER_NUM; i++) {
+                       mode = (i == MSM_DSI_CMD_ENCODER_ID) ?
+                               MDP5_INTF_DSI_MODE_COMMAND :
+                               MDP5_INTF_DSI_MODE_VIDEO;
+                       dsi_encs[i] = construct_encoder(mdp5_kms, INTF_DSI,
+                                                       intf_num, mode);
+                       if (IS_ERR(dsi_encs)) {
+                               ret = PTR_ERR(dsi_encs);
+                               break;
+                       }
+               }
+
+               ret = msm_dsi_modeset_init(priv->dsi[dsi_id], dev, dsi_encs);
+               break;
+       }
        default:
                dev_err(dev->dev, "unknown intf: %d\n", intf_type);
                ret = -EINVAL;
index 6efa5c61aa9809d3492b6f985f84c55fd85d8c47..2c0de174cc09b9e198d05e3eed1745e193d997ba 100644 (file)
@@ -159,10 +159,9 @@ static inline uint32_t intf2err(int intf_num)
        }
 }
 
+#define GET_PING_PONG_ID(layer_mixer)  ((layer_mixer == 5) ? 3 : layer_mixer)
 static inline uint32_t intf2vblank(int lm, struct mdp5_interface *intf)
 {
-#define GET_PING_PONG_ID(layer_mixer)  ((layer_mixer == 5) ? 3 : layer_mixer)
-
        /*
         * In case of DSI Command Mode, the Ping Pong's read pointer IRQ
         * acts as a Vblank signal. The Ping Pong buffer used is bound to
@@ -185,6 +184,11 @@ static inline uint32_t intf2vblank(int lm, struct mdp5_interface *intf)
        }
 }
 
+static inline uint32_t lm2ppdone(int lm)
+{
+       return MDP5_IRQ_PING_PONG_0_DONE << GET_PING_PONG_ID(lm);
+}
+
 int mdp5_disable(struct mdp5_kms *mdp5_kms);
 int mdp5_enable(struct mdp5_kms *mdp5_kms);
 
@@ -238,5 +242,25 @@ struct drm_crtc *mdp5_crtc_init(struct drm_device *dev,
 
 struct drm_encoder *mdp5_encoder_init(struct drm_device *dev,
                struct mdp5_interface *intf);
+int mdp5_encoder_set_split_display(struct drm_encoder *encoder,
+                                       struct drm_encoder *slave_encoder);
+
+#ifdef CONFIG_DRM_MSM_DSI
+struct drm_encoder *mdp5_cmd_encoder_init(struct drm_device *dev,
+                               struct mdp5_interface *intf);
+int mdp5_cmd_encoder_set_split_display(struct drm_encoder *encoder,
+                                       struct drm_encoder *slave_encoder);
+#else
+static inline struct drm_encoder *mdp5_cmd_encoder_init(
+                       struct drm_device *dev, struct mdp5_interface *intf)
+{
+       return ERR_PTR(-EINVAL);
+}
+static inline int mdp5_cmd_encoder_set_split_display(
+       struct drm_encoder *encoder, struct drm_encoder *slave_encoder)
+{
+       return -EINVAL;
+}
+#endif
 
 #endif /* __MDP5_KMS_H__ */
index 0c38f34066e5582d0e7cb3f1198bc50c034ecf97..47f4dd407671970fc247c4a9999dcccc03202c8e 100644 (file)
@@ -1076,6 +1076,7 @@ static struct platform_driver msm_platform_driver = {
 static int __init msm_drm_register(void)
 {
        DBG("init");
+       msm_dsi_register();
        msm_edp_register();
        hdmi_register();
        adreno_register();
@@ -1089,6 +1090,7 @@ static void __exit msm_drm_unregister(void)
        hdmi_unregister();
        adreno_unregister();
        msm_edp_unregister();
+       msm_dsi_unregister();
 }
 
 module_init(msm_drm_register);