aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHai Li <hali@codeaurora.org>2015-03-26 19:25:17 -0400
committerRob Clark <robdclark@gmail.com>2015-04-01 19:29:38 -0400
commitd5af49c92a8aff8236e7b0bb35e9af364000c017 (patch)
treef7cd9e3bbb0a452ee1f3868b29f71caefdbbfc1c
parenta689554ba6ed81cf606c16539f6ffc2a1dcdaf8e (diff)
drm/msm/mdp5: Enable DSI connector in msm drm driver
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>
-rw-r--r--drivers/gpu/drm/msm/Makefile3
-rw-r--r--drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.c4
-rw-r--r--drivers/gpu/drm/msm/mdp/mdp5/mdp5_cmd_encoder.c343
-rw-r--r--drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c11
-rw-r--r--drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c43
-rw-r--r--drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c70
-rw-r--r--drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h28
-rw-r--r--drivers/gpu/drm/msm/msm_drv.c2
8 files changed, 497 insertions, 7 deletions
diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
index 5c144cc5f8db..ab2086783fee 100644
--- a/drivers/gpu/drm/msm/Makefile
+++ b/drivers/gpu/drm/msm/Makefile
@@ -53,6 +53,7 @@ msm-$(CONFIG_COMMON_CLK) += mdp/mdp4/mdp4_lvds_pll.o
53msm-$(CONFIG_DRM_MSM_DSI) += dsi/dsi.o \ 53msm-$(CONFIG_DRM_MSM_DSI) += dsi/dsi.o \
54 dsi/dsi_host.o \ 54 dsi/dsi_host.o \
55 dsi/dsi_manager.o \ 55 dsi/dsi_manager.o \
56 dsi/dsi_phy.o 56 dsi/dsi_phy.o \
57 mdp/mdp5/mdp5_cmd_encoder.o
57 58
58obj-$(CONFIG_DRM_MSM) += msm.o 59obj-$(CONFIG_DRM_MSM) += msm.o
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.c
index 56dfc5b626dd..e001e6b2296a 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.c
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.c
@@ -77,6 +77,8 @@ const struct mdp5_cfg_hw msm8x74_config = {
77 }, 77 },
78 .intfs = { 78 .intfs = {
79 [0] = INTF_eDP, 79 [0] = INTF_eDP,
80 [1] = INTF_DSI,
81 [2] = INTF_DSI,
80 [3] = INTF_HDMI, 82 [3] = INTF_HDMI,
81 }, 83 },
82 .max_clk = 200000000, 84 .max_clk = 200000000,
@@ -145,6 +147,8 @@ const struct mdp5_cfg_hw apq8084_config = {
145 }, 147 },
146 .intfs = { 148 .intfs = {
147 [0] = INTF_eDP, 149 [0] = INTF_eDP,
150 [1] = INTF_DSI,
151 [2] = INTF_DSI,
148 [3] = INTF_HDMI, 152 [3] = INTF_HDMI,
149 }, 153 },
150 .max_clk = 320000000, 154 .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
index 000000000000..e4e89567f51d
--- /dev/null
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cmd_encoder.c
@@ -0,0 +1,343 @@
1/*
2 * Copyright (c) 2015, The Linux Foundation. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 and
6 * only version 2 as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
14#include "mdp5_kms.h"
15
16#include "drm_crtc.h"
17#include "drm_crtc_helper.h"
18
19struct mdp5_cmd_encoder {
20 struct drm_encoder base;
21 struct mdp5_interface intf;
22 bool enabled;
23 uint32_t bsc;
24};
25#define to_mdp5_cmd_encoder(x) container_of(x, struct mdp5_cmd_encoder, base)
26
27static struct mdp5_kms *get_kms(struct drm_encoder *encoder)
28{
29 struct msm_drm_private *priv = encoder->dev->dev_private;
30 return to_mdp5_kms(to_mdp_kms(priv->kms));
31}
32
33#ifdef CONFIG_MSM_BUS_SCALING
34#include <mach/board.h>
35#include <linux/msm-bus.h>
36#include <linux/msm-bus-board.h>
37#define MDP_BUS_VECTOR_ENTRY(ab_val, ib_val) \
38 { \
39 .src = MSM_BUS_MASTER_MDP_PORT0, \
40 .dst = MSM_BUS_SLAVE_EBI_CH0, \
41 .ab = (ab_val), \
42 .ib = (ib_val), \
43 }
44
45static struct msm_bus_vectors mdp_bus_vectors[] = {
46 MDP_BUS_VECTOR_ENTRY(0, 0),
47 MDP_BUS_VECTOR_ENTRY(2000000000, 2000000000),
48};
49static struct msm_bus_paths mdp_bus_usecases[] = { {
50 .num_paths = 1,
51 .vectors = &mdp_bus_vectors[0],
52}, {
53 .num_paths = 1,
54 .vectors = &mdp_bus_vectors[1],
55} };
56static struct msm_bus_scale_pdata mdp_bus_scale_table = {
57 .usecase = mdp_bus_usecases,
58 .num_usecases = ARRAY_SIZE(mdp_bus_usecases),
59 .name = "mdss_mdp",
60};
61
62static void bs_init(struct mdp5_cmd_encoder *mdp5_cmd_enc)
63{
64 mdp5_cmd_enc->bsc = msm_bus_scale_register_client(
65 &mdp_bus_scale_table);
66 DBG("bus scale client: %08x", mdp5_cmd_enc->bsc);
67}
68
69static void bs_fini(struct mdp5_cmd_encoder *mdp5_cmd_enc)
70{
71 if (mdp5_cmd_enc->bsc) {
72 msm_bus_scale_unregister_client(mdp5_cmd_enc->bsc);
73 mdp5_cmd_enc->bsc = 0;
74 }
75}
76
77static void bs_set(struct mdp5_cmd_encoder *mdp5_cmd_enc, int idx)
78{
79 if (mdp5_cmd_enc->bsc) {
80 DBG("set bus scaling: %d", idx);
81 /* HACK: scaling down, and then immediately back up
82 * seems to leave things broken (underflow).. so
83 * never disable:
84 */
85 idx = 1;
86 msm_bus_scale_client_update_request(mdp5_cmd_enc->bsc, idx);
87 }
88}
89#else
90static void bs_init(struct mdp5_cmd_encoder *mdp5_cmd_enc) {}
91static void bs_fini(struct mdp5_cmd_encoder *mdp5_cmd_enc) {}
92static void bs_set(struct mdp5_cmd_encoder *mdp5_cmd_enc, int idx) {}
93#endif
94
95#define VSYNC_CLK_RATE 19200000
96static int pingpong_tearcheck_setup(struct drm_encoder *encoder,
97 struct drm_display_mode *mode)
98{
99 struct mdp5_kms *mdp5_kms = get_kms(encoder);
100 struct device *dev = encoder->dev->dev;
101 u32 total_lines_x100, vclks_line, cfg;
102 long vsync_clk_speed;
103 int pp_id = GET_PING_PONG_ID(mdp5_crtc_get_lm(encoder->crtc));
104
105 if (IS_ERR_OR_NULL(mdp5_kms->vsync_clk)) {
106 dev_err(dev, "vsync_clk is not initialized\n");
107 return -EINVAL;
108 }
109
110 total_lines_x100 = mode->vtotal * mode->vrefresh;
111 if (!total_lines_x100) {
112 dev_err(dev, "%s: vtotal(%d) or vrefresh(%d) is 0\n",
113 __func__, mode->vtotal, mode->vrefresh);
114 return -EINVAL;
115 }
116
117 vsync_clk_speed = clk_round_rate(mdp5_kms->vsync_clk, VSYNC_CLK_RATE);
118 if (vsync_clk_speed <= 0) {
119 dev_err(dev, "vsync_clk round rate failed %ld\n",
120 vsync_clk_speed);
121 return -EINVAL;
122 }
123 vclks_line = vsync_clk_speed * 100 / total_lines_x100;
124
125 cfg = MDP5_PP_SYNC_CONFIG_VSYNC_COUNTER_EN
126 | MDP5_PP_SYNC_CONFIG_VSYNC_IN_EN;
127 cfg |= MDP5_PP_SYNC_CONFIG_VSYNC_COUNT(vclks_line);
128
129 mdp5_write(mdp5_kms, REG_MDP5_PP_SYNC_CONFIG_VSYNC(pp_id), cfg);
130 mdp5_write(mdp5_kms,
131 REG_MDP5_PP_SYNC_CONFIG_HEIGHT(pp_id), 0xfff0);
132 mdp5_write(mdp5_kms,
133 REG_MDP5_PP_VSYNC_INIT_VAL(pp_id), mode->vdisplay);
134 mdp5_write(mdp5_kms, REG_MDP5_PP_RD_PTR_IRQ(pp_id), mode->vdisplay + 1);
135 mdp5_write(mdp5_kms, REG_MDP5_PP_START_POS(pp_id), mode->vdisplay);
136 mdp5_write(mdp5_kms, REG_MDP5_PP_SYNC_THRESH(pp_id),
137 MDP5_PP_SYNC_THRESH_START(4) |
138 MDP5_PP_SYNC_THRESH_CONTINUE(4));
139
140 return 0;
141}
142
143static int pingpong_tearcheck_enable(struct drm_encoder *encoder)
144{
145 struct mdp5_kms *mdp5_kms = get_kms(encoder);
146 int pp_id = GET_PING_PONG_ID(mdp5_crtc_get_lm(encoder->crtc));
147 int ret;
148
149 ret = clk_set_rate(mdp5_kms->vsync_clk,
150 clk_round_rate(mdp5_kms->vsync_clk, VSYNC_CLK_RATE));
151 if (ret) {
152 dev_err(encoder->dev->dev,
153 "vsync_clk clk_set_rate failed, %d\n", ret);
154 return ret;
155 }
156 ret = clk_prepare_enable(mdp5_kms->vsync_clk);
157 if (ret) {
158 dev_err(encoder->dev->dev,
159 "vsync_clk clk_prepare_enable failed, %d\n", ret);
160 return ret;
161 }
162
163 mdp5_write(mdp5_kms, REG_MDP5_PP_TEAR_CHECK_EN(pp_id), 1);
164
165 return 0;
166}
167
168static void pingpong_tearcheck_disable(struct drm_encoder *encoder)
169{
170 struct mdp5_kms *mdp5_kms = get_kms(encoder);
171 int pp_id = GET_PING_PONG_ID(mdp5_crtc_get_lm(encoder->crtc));
172
173 mdp5_write(mdp5_kms, REG_MDP5_PP_TEAR_CHECK_EN(pp_id), 0);
174 clk_disable_unprepare(mdp5_kms->vsync_clk);
175}
176
177static void mdp5_cmd_encoder_destroy(struct drm_encoder *encoder)
178{
179 struct mdp5_cmd_encoder *mdp5_cmd_enc = to_mdp5_cmd_encoder(encoder);
180 bs_fini(mdp5_cmd_enc);
181 drm_encoder_cleanup(encoder);
182 kfree(mdp5_cmd_enc);
183}
184
185static const struct drm_encoder_funcs mdp5_cmd_encoder_funcs = {
186 .destroy = mdp5_cmd_encoder_destroy,
187};
188
189static bool mdp5_cmd_encoder_mode_fixup(struct drm_encoder *encoder,
190 const struct drm_display_mode *mode,
191 struct drm_display_mode *adjusted_mode)
192{
193 return true;
194}
195
196static void mdp5_cmd_encoder_mode_set(struct drm_encoder *encoder,
197 struct drm_display_mode *mode,
198 struct drm_display_mode *adjusted_mode)
199{
200 struct mdp5_cmd_encoder *mdp5_cmd_enc = to_mdp5_cmd_encoder(encoder);
201
202 mode = adjusted_mode;
203
204 DBG("set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x",
205 mode->base.id, mode->name,
206 mode->vrefresh, mode->clock,
207 mode->hdisplay, mode->hsync_start,
208 mode->hsync_end, mode->htotal,
209 mode->vdisplay, mode->vsync_start,
210 mode->vsync_end, mode->vtotal,
211 mode->type, mode->flags);
212 pingpong_tearcheck_setup(encoder, mode);
213 mdp5_crtc_set_intf(encoder->crtc, &mdp5_cmd_enc->intf);
214}
215
216static void mdp5_cmd_encoder_disable(struct drm_encoder *encoder)
217{
218 struct mdp5_cmd_encoder *mdp5_cmd_enc = to_mdp5_cmd_encoder(encoder);
219 struct mdp5_kms *mdp5_kms = get_kms(encoder);
220 struct mdp5_ctl *ctl = mdp5_crtc_get_ctl(encoder->crtc);
221 struct mdp5_interface *intf = &mdp5_cmd_enc->intf;
222 int lm = mdp5_crtc_get_lm(encoder->crtc);
223
224 if (WARN_ON(!mdp5_cmd_enc->enabled))
225 return;
226
227 /* Wait for the last frame done */
228 mdp_irq_wait(&mdp5_kms->base, lm2ppdone(lm));
229 pingpong_tearcheck_disable(encoder);
230
231 mdp5_ctl_set_encoder_state(ctl, false);
232 mdp5_ctl_commit(ctl, mdp_ctl_flush_mask_encoder(intf));
233
234 bs_set(mdp5_cmd_enc, 0);
235
236 mdp5_cmd_enc->enabled = false;
237}
238
239static void mdp5_cmd_encoder_enable(struct drm_encoder *encoder)
240{
241 struct mdp5_cmd_encoder *mdp5_cmd_enc = to_mdp5_cmd_encoder(encoder);
242 struct mdp5_ctl *ctl = mdp5_crtc_get_ctl(encoder->crtc);
243 struct mdp5_interface *intf = &mdp5_cmd_enc->intf;
244
245 if (WARN_ON(mdp5_cmd_enc->enabled))
246 return;
247
248 bs_set(mdp5_cmd_enc, 1);
249 if (pingpong_tearcheck_enable(encoder))
250 return;
251
252 mdp5_ctl_commit(ctl, mdp_ctl_flush_mask_encoder(intf));
253
254 mdp5_ctl_set_encoder_state(ctl, true);
255
256 mdp5_cmd_enc->enabled = true;
257}
258
259static const struct drm_encoder_helper_funcs mdp5_cmd_encoder_helper_funcs = {
260 .mode_fixup = mdp5_cmd_encoder_mode_fixup,
261 .mode_set = mdp5_cmd_encoder_mode_set,
262 .disable = mdp5_cmd_encoder_disable,
263 .enable = mdp5_cmd_encoder_enable,
264};
265
266int mdp5_cmd_encoder_set_split_display(struct drm_encoder *encoder,
267 struct drm_encoder *slave_encoder)
268{
269 struct mdp5_cmd_encoder *mdp5_cmd_enc = to_mdp5_cmd_encoder(encoder);
270 struct mdp5_kms *mdp5_kms;
271 int intf_num;
272 u32 data = 0;
273
274 if (!encoder || !slave_encoder)
275 return -EINVAL;
276
277 mdp5_kms = get_kms(encoder);
278 intf_num = mdp5_cmd_enc->intf.num;
279
280 /* Switch slave encoder's trigger MUX, to use the master's
281 * start signal for the slave encoder
282 */
283 if (intf_num == 1)
284 data |= MDP5_SPLIT_DPL_UPPER_INTF2_SW_TRG_MUX;
285 else if (intf_num == 2)
286 data |= MDP5_SPLIT_DPL_UPPER_INTF1_SW_TRG_MUX;
287 else
288 return -EINVAL;
289
290 /* Smart Panel, Sync mode */
291 data |= MDP5_SPLIT_DPL_UPPER_SMART_PANEL;
292
293 /* Make sure clocks are on when connectors calling this function. */
294 mdp5_enable(mdp5_kms);
295 mdp5_write(mdp5_kms, REG_MDP5_SPLIT_DPL_UPPER, data);
296
297 mdp5_write(mdp5_kms, REG_MDP5_SPLIT_DPL_LOWER,
298 MDP5_SPLIT_DPL_LOWER_SMART_PANEL);
299 mdp5_write(mdp5_kms, REG_MDP5_SPLIT_DPL_EN, 1);
300 mdp5_disable(mdp5_kms);
301
302 return 0;
303}
304
305/* initialize command mode encoder */
306struct drm_encoder *mdp5_cmd_encoder_init(struct drm_device *dev,
307 struct mdp5_interface *intf)
308{
309 struct drm_encoder *encoder = NULL;
310 struct mdp5_cmd_encoder *mdp5_cmd_enc;
311 int ret;
312
313 if (WARN_ON((intf->type != INTF_DSI) &&
314 (intf->mode != MDP5_INTF_DSI_MODE_COMMAND))) {
315 ret = -EINVAL;
316 goto fail;
317 }
318
319 mdp5_cmd_enc = kzalloc(sizeof(*mdp5_cmd_enc), GFP_KERNEL);
320 if (!mdp5_cmd_enc) {
321 ret = -ENOMEM;
322 goto fail;
323 }
324
325 memcpy(&mdp5_cmd_enc->intf, intf, sizeof(mdp5_cmd_enc->intf));
326 encoder = &mdp5_cmd_enc->base;
327
328 drm_encoder_init(dev, encoder, &mdp5_cmd_encoder_funcs,
329 DRM_MODE_ENCODER_DSI);
330
331 drm_encoder_helper_add(encoder, &mdp5_cmd_encoder_helper_funcs);
332
333 bs_init(mdp5_cmd_enc);
334
335 return encoder;
336
337fail:
338 if (encoder)
339 mdp5_cmd_encoder_destroy(encoder);
340
341 return ERR_PTR(ret);
342}
343
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
index 9527ad112446..c1530772187d 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
@@ -622,7 +622,16 @@ void mdp5_crtc_set_intf(struct drm_crtc *crtc, struct mdp5_interface *intf)
622 622
623 /* now that we know what irq's we want: */ 623 /* now that we know what irq's we want: */
624 mdp5_crtc->err.irqmask = intf2err(intf->num); 624 mdp5_crtc->err.irqmask = intf2err(intf->num);
625 mdp5_crtc->vblank.irqmask = intf2vblank(lm, intf); 625
626 /* Register command mode Pingpong done as vblank for now,
627 * so that atomic commit should wait for it to finish.
628 * Ideally, in the future, we should take rd_ptr done as vblank,
629 * and let atomic commit wait for pingpong done for commond mode.
630 */
631 if (intf->mode == MDP5_INTF_DSI_MODE_COMMAND)
632 mdp5_crtc->vblank.irqmask = lm2ppdone(lm);
633 else
634 mdp5_crtc->vblank.irqmask = intf2vblank(lm, intf);
626 mdp_irq_update(&mdp5_kms->base); 635 mdp_irq_update(&mdp5_kms->base);
627 636
628 mdp5_ctl_set_intf(mdp5_crtc->ctl, intf); 637 mdp5_ctl_set_intf(mdp5_crtc->ctl, intf);
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c
index 2ef6d1b0a218..1188f4bf1e60 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c
@@ -286,12 +286,51 @@ static const struct drm_encoder_helper_funcs mdp5_encoder_helper_funcs = {
286 .enable = mdp5_encoder_enable, 286 .enable = mdp5_encoder_enable,
287}; 287};
288 288
289int mdp5_encoder_set_split_display(struct drm_encoder *encoder,
290 struct drm_encoder *slave_encoder)
291{
292 struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder);
293 struct mdp5_kms *mdp5_kms;
294 int intf_num;
295 u32 data = 0;
296
297 if (!encoder || !slave_encoder)
298 return -EINVAL;
299
300 mdp5_kms = get_kms(encoder);
301 intf_num = mdp5_encoder->intf.num;
302
303 /* Switch slave encoder's TimingGen Sync mode,
304 * to use the master's enable signal for the slave encoder.
305 */
306 if (intf_num == 1)
307 data |= MDP5_SPLIT_DPL_LOWER_INTF2_TG_SYNC;
308 else if (intf_num == 2)
309 data |= MDP5_SPLIT_DPL_LOWER_INTF1_TG_SYNC;
310 else
311 return -EINVAL;
312
313 /* Make sure clocks are on when connectors calling this function. */
314 mdp5_enable(mdp5_kms);
315 mdp5_write(mdp5_kms, REG_MDP5_MDP_SPARE_0(0),
316 MDP5_MDP_SPARE_0_SPLIT_DPL_SINGLE_FLUSH_EN);
317 /* Dumb Panel, Sync mode */
318 mdp5_write(mdp5_kms, REG_MDP5_SPLIT_DPL_UPPER, 0);
319 mdp5_write(mdp5_kms, REG_MDP5_SPLIT_DPL_LOWER, data);
320 mdp5_write(mdp5_kms, REG_MDP5_SPLIT_DPL_EN, 1);
321 mdp5_disable(mdp5_kms);
322
323 return 0;
324}
325
289/* initialize encoder */ 326/* initialize encoder */
290struct drm_encoder *mdp5_encoder_init(struct drm_device *dev, 327struct drm_encoder *mdp5_encoder_init(struct drm_device *dev,
291 struct mdp5_interface *intf) 328 struct mdp5_interface *intf)
292{ 329{
293 struct drm_encoder *encoder = NULL; 330 struct drm_encoder *encoder = NULL;
294 struct mdp5_encoder *mdp5_encoder; 331 struct mdp5_encoder *mdp5_encoder;
332 int enc_type = (intf->type == INTF_DSI) ?
333 DRM_MODE_ENCODER_DSI : DRM_MODE_ENCODER_TMDS;
295 int ret; 334 int ret;
296 335
297 mdp5_encoder = kzalloc(sizeof(*mdp5_encoder), GFP_KERNEL); 336 mdp5_encoder = kzalloc(sizeof(*mdp5_encoder), GFP_KERNEL);
@@ -305,8 +344,8 @@ struct drm_encoder *mdp5_encoder_init(struct drm_device *dev,
305 344
306 spin_lock_init(&mdp5_encoder->intf_lock); 345 spin_lock_init(&mdp5_encoder->intf_lock);
307 346
308 drm_encoder_init(dev, encoder, &mdp5_encoder_funcs, 347 drm_encoder_init(dev, encoder, &mdp5_encoder_funcs, enc_type);
309 DRM_MODE_ENCODER_TMDS); 348
310 drm_encoder_helper_add(encoder, &mdp5_encoder_helper_funcs); 349 drm_encoder_helper_add(encoder, &mdp5_encoder_helper_funcs);
311 350
312 bs_init(mdp5_encoder); 351 bs_init(mdp5_encoder);
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c
index 4d8f5b471e4d..dfa8beb9343a 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c
@@ -86,6 +86,18 @@ static long mdp5_round_pixclk(struct msm_kms *kms, unsigned long rate,
86 return rate; 86 return rate;
87} 87}
88 88
89static int mdp5_set_split_display(struct msm_kms *kms,
90 struct drm_encoder *encoder,
91 struct drm_encoder *slave_encoder,
92 bool is_cmd_mode)
93{
94 if (is_cmd_mode)
95 return mdp5_cmd_encoder_set_split_display(encoder,
96 slave_encoder);
97 else
98 return mdp5_encoder_set_split_display(encoder, slave_encoder);
99}
100
89static void mdp5_preclose(struct msm_kms *kms, struct drm_file *file) 101static void mdp5_preclose(struct msm_kms *kms, struct drm_file *file)
90{ 102{
91 struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms)); 103 struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms));
@@ -131,6 +143,7 @@ static const struct mdp_kms_funcs kms_funcs = {
131 .complete_commit = mdp5_complete_commit, 143 .complete_commit = mdp5_complete_commit,
132 .get_format = mdp_get_format, 144 .get_format = mdp_get_format,
133 .round_pixclk = mdp5_round_pixclk, 145 .round_pixclk = mdp5_round_pixclk,
146 .set_split_display = mdp5_set_split_display,
134 .preclose = mdp5_preclose, 147 .preclose = mdp5_preclose,
135 .destroy = mdp5_destroy, 148 .destroy = mdp5_destroy,
136 }, 149 },
@@ -174,7 +187,12 @@ static struct drm_encoder *construct_encoder(struct mdp5_kms *mdp5_kms,
174 .mode = intf_mode, 187 .mode = intf_mode,
175 }; 188 };
176 189
177 encoder = mdp5_encoder_init(dev, &intf); 190 if ((intf_type == INTF_DSI) &&
191 (intf_mode == MDP5_INTF_DSI_MODE_COMMAND))
192 encoder = mdp5_cmd_encoder_init(dev, &intf);
193 else
194 encoder = mdp5_encoder_init(dev, &intf);
195
178 if (IS_ERR(encoder)) { 196 if (IS_ERR(encoder)) {
179 dev_err(dev->dev, "failed to construct encoder\n"); 197 dev_err(dev->dev, "failed to construct encoder\n");
180 return encoder; 198 return encoder;
@@ -186,6 +204,24 @@ static struct drm_encoder *construct_encoder(struct mdp5_kms *mdp5_kms,
186 return encoder; 204 return encoder;
187} 205}
188 206
207static int get_dsi_id_from_intf(const struct mdp5_cfg_hw *hw_cfg, int intf_num)
208{
209 const int intf_cnt = hw_cfg->intf.count;
210 const u32 *intfs = hw_cfg->intfs;
211 int id = 0, i;
212
213 for (i = 0; i < intf_cnt; i++) {
214 if (intfs[i] == INTF_DSI) {
215 if (intf_num == i)
216 return id;
217
218 id++;
219 }
220 }
221
222 return -EINVAL;
223}
224
189static int modeset_init_intf(struct mdp5_kms *mdp5_kms, int intf_num) 225static int modeset_init_intf(struct mdp5_kms *mdp5_kms, int intf_num)
190{ 226{
191 struct drm_device *dev = mdp5_kms->dev; 227 struct drm_device *dev = mdp5_kms->dev;
@@ -225,6 +261,38 @@ static int modeset_init_intf(struct mdp5_kms *mdp5_kms, int intf_num)
225 261
226 ret = hdmi_modeset_init(priv->hdmi, dev, encoder); 262 ret = hdmi_modeset_init(priv->hdmi, dev, encoder);
227 break; 263 break;
264 case INTF_DSI:
265 {
266 int dsi_id = get_dsi_id_from_intf(hw_cfg, intf_num);
267 struct drm_encoder *dsi_encs[MSM_DSI_ENCODER_NUM];
268 enum mdp5_intf_mode mode;
269 int i;
270
271 if ((dsi_id >= ARRAY_SIZE(priv->dsi)) || (dsi_id < 0)) {
272 dev_err(dev->dev, "failed to find dsi from intf %d\n",
273 intf_num);
274 ret = -EINVAL;
275 break;
276 }
277
278 if (!priv->dsi[dsi_id])
279 break;
280
281 for (i = 0; i < MSM_DSI_ENCODER_NUM; i++) {
282 mode = (i == MSM_DSI_CMD_ENCODER_ID) ?
283 MDP5_INTF_DSI_MODE_COMMAND :
284 MDP5_INTF_DSI_MODE_VIDEO;
285 dsi_encs[i] = construct_encoder(mdp5_kms, INTF_DSI,
286 intf_num, mode);
287 if (IS_ERR(dsi_encs)) {
288 ret = PTR_ERR(dsi_encs);
289 break;
290 }
291 }
292
293 ret = msm_dsi_modeset_init(priv->dsi[dsi_id], dev, dsi_encs);
294 break;
295 }
228 default: 296 default:
229 dev_err(dev->dev, "unknown intf: %d\n", intf_type); 297 dev_err(dev->dev, "unknown intf: %d\n", intf_type);
230 ret = -EINVAL; 298 ret = -EINVAL;
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h
index 6efa5c61aa98..2c0de174cc09 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h
@@ -159,10 +159,9 @@ static inline uint32_t intf2err(int intf_num)
159 } 159 }
160} 160}
161 161
162#define GET_PING_PONG_ID(layer_mixer) ((layer_mixer == 5) ? 3 : layer_mixer)
162static inline uint32_t intf2vblank(int lm, struct mdp5_interface *intf) 163static inline uint32_t intf2vblank(int lm, struct mdp5_interface *intf)
163{ 164{
164#define GET_PING_PONG_ID(layer_mixer) ((layer_mixer == 5) ? 3 : layer_mixer)
165
166 /* 165 /*
167 * In case of DSI Command Mode, the Ping Pong's read pointer IRQ 166 * In case of DSI Command Mode, the Ping Pong's read pointer IRQ
168 * acts as a Vblank signal. The Ping Pong buffer used is bound to 167 * 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)
185 } 184 }
186} 185}
187 186
187static inline uint32_t lm2ppdone(int lm)
188{
189 return MDP5_IRQ_PING_PONG_0_DONE << GET_PING_PONG_ID(lm);
190}
191
188int mdp5_disable(struct mdp5_kms *mdp5_kms); 192int mdp5_disable(struct mdp5_kms *mdp5_kms);
189int mdp5_enable(struct mdp5_kms *mdp5_kms); 193int mdp5_enable(struct mdp5_kms *mdp5_kms);
190 194
@@ -238,5 +242,25 @@ struct drm_crtc *mdp5_crtc_init(struct drm_device *dev,
238 242
239struct drm_encoder *mdp5_encoder_init(struct drm_device *dev, 243struct drm_encoder *mdp5_encoder_init(struct drm_device *dev,
240 struct mdp5_interface *intf); 244 struct mdp5_interface *intf);
245int mdp5_encoder_set_split_display(struct drm_encoder *encoder,
246 struct drm_encoder *slave_encoder);
247
248#ifdef CONFIG_DRM_MSM_DSI
249struct drm_encoder *mdp5_cmd_encoder_init(struct drm_device *dev,
250 struct mdp5_interface *intf);
251int mdp5_cmd_encoder_set_split_display(struct drm_encoder *encoder,
252 struct drm_encoder *slave_encoder);
253#else
254static inline struct drm_encoder *mdp5_cmd_encoder_init(
255 struct drm_device *dev, struct mdp5_interface *intf)
256{
257 return ERR_PTR(-EINVAL);
258}
259static inline int mdp5_cmd_encoder_set_split_display(
260 struct drm_encoder *encoder, struct drm_encoder *slave_encoder)
261{
262 return -EINVAL;
263}
264#endif
241 265
242#endif /* __MDP5_KMS_H__ */ 266#endif /* __MDP5_KMS_H__ */
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c
index 0c38f34066e5..47f4dd407671 100644
--- a/drivers/gpu/drm/msm/msm_drv.c
+++ b/drivers/gpu/drm/msm/msm_drv.c
@@ -1076,6 +1076,7 @@ static struct platform_driver msm_platform_driver = {
1076static int __init msm_drm_register(void) 1076static int __init msm_drm_register(void)
1077{ 1077{
1078 DBG("init"); 1078 DBG("init");
1079 msm_dsi_register();
1079 msm_edp_register(); 1080 msm_edp_register();
1080 hdmi_register(); 1081 hdmi_register();
1081 adreno_register(); 1082 adreno_register();
@@ -1089,6 +1090,7 @@ static void __exit msm_drm_unregister(void)
1089 hdmi_unregister(); 1090 hdmi_unregister();
1090 adreno_unregister(); 1091 adreno_unregister();
1091 msm_edp_unregister(); 1092 msm_edp_unregister();
1093 msm_dsi_unregister();
1092} 1094}
1093 1095
1094module_init(msm_drm_register); 1096module_init(msm_drm_register);