diff options
-rw-r--r-- | drivers/gpu/drm/msm/Makefile | 3 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.c | 4 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/mdp/mdp5/mdp5_cmd_encoder.c | 343 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c | 11 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c | 43 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c | 70 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h | 28 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/msm_drv.c | 2 |
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 | |||
53 | msm-$(CONFIG_DRM_MSM_DSI) += dsi/dsi.o \ | 53 | msm-$(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 | ||
58 | obj-$(CONFIG_DRM_MSM) += msm.o | 59 | obj-$(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 | |||
19 | struct 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 | |||
27 | static 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 | |||
45 | static struct msm_bus_vectors mdp_bus_vectors[] = { | ||
46 | MDP_BUS_VECTOR_ENTRY(0, 0), | ||
47 | MDP_BUS_VECTOR_ENTRY(2000000000, 2000000000), | ||
48 | }; | ||
49 | static 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 | } }; | ||
56 | static 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 | |||
62 | static 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 | |||
69 | static 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 | |||
77 | static 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 | ||
90 | static void bs_init(struct mdp5_cmd_encoder *mdp5_cmd_enc) {} | ||
91 | static void bs_fini(struct mdp5_cmd_encoder *mdp5_cmd_enc) {} | ||
92 | static void bs_set(struct mdp5_cmd_encoder *mdp5_cmd_enc, int idx) {} | ||
93 | #endif | ||
94 | |||
95 | #define VSYNC_CLK_RATE 19200000 | ||
96 | static 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 | |||
143 | static 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 | |||
168 | static 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 | |||
177 | static 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 | |||
185 | static const struct drm_encoder_funcs mdp5_cmd_encoder_funcs = { | ||
186 | .destroy = mdp5_cmd_encoder_destroy, | ||
187 | }; | ||
188 | |||
189 | static 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 | |||
196 | static 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 | |||
216 | static 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 | |||
239 | static 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 | |||
259 | static 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 | |||
266 | int 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 */ | ||
306 | struct 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 | |||
337 | fail: | ||
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 | ||
289 | int 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 */ |
290 | struct drm_encoder *mdp5_encoder_init(struct drm_device *dev, | 327 | struct 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 | ||
89 | static 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 | |||
89 | static void mdp5_preclose(struct msm_kms *kms, struct drm_file *file) | 101 | static 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 | ||
207 | static 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 | |||
189 | static int modeset_init_intf(struct mdp5_kms *mdp5_kms, int intf_num) | 225 | static 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) | ||
162 | static inline uint32_t intf2vblank(int lm, struct mdp5_interface *intf) | 163 | static 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 | ||
187 | static inline uint32_t lm2ppdone(int lm) | ||
188 | { | ||
189 | return MDP5_IRQ_PING_PONG_0_DONE << GET_PING_PONG_ID(lm); | ||
190 | } | ||
191 | |||
188 | int mdp5_disable(struct mdp5_kms *mdp5_kms); | 192 | int mdp5_disable(struct mdp5_kms *mdp5_kms); |
189 | int mdp5_enable(struct mdp5_kms *mdp5_kms); | 193 | int 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 | ||
239 | struct drm_encoder *mdp5_encoder_init(struct drm_device *dev, | 243 | struct drm_encoder *mdp5_encoder_init(struct drm_device *dev, |
240 | struct mdp5_interface *intf); | 244 | struct mdp5_interface *intf); |
245 | int mdp5_encoder_set_split_display(struct drm_encoder *encoder, | ||
246 | struct drm_encoder *slave_encoder); | ||
247 | |||
248 | #ifdef CONFIG_DRM_MSM_DSI | ||
249 | struct drm_encoder *mdp5_cmd_encoder_init(struct drm_device *dev, | ||
250 | struct mdp5_interface *intf); | ||
251 | int mdp5_cmd_encoder_set_split_display(struct drm_encoder *encoder, | ||
252 | struct drm_encoder *slave_encoder); | ||
253 | #else | ||
254 | static inline struct drm_encoder *mdp5_cmd_encoder_init( | ||
255 | struct drm_device *dev, struct mdp5_interface *intf) | ||
256 | { | ||
257 | return ERR_PTR(-EINVAL); | ||
258 | } | ||
259 | static 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 = { | |||
1076 | static int __init msm_drm_register(void) | 1076 | static 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 | ||
1094 | module_init(msm_drm_register); | 1096 | module_init(msm_drm_register); |