aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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);