]>
Commit | Line | Data |
---|---|---|
06c0dd96 | 1 | /* |
00453981 | 2 | * Copyright (c) 2014, The Linux Foundation. All rights reserved. |
06c0dd96 RC |
3 | * Copyright (C) 2013 Red Hat |
4 | * Author: Rob Clark <robdclark@gmail.com> | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify it | |
7 | * under the terms of the GNU General Public License version 2 as published by | |
8 | * the Free Software Foundation. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, but WITHOUT | |
11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
13 | * more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License along with | |
16 | * this program. If not, see <http://www.gnu.org/licenses/>. | |
17 | */ | |
18 | ||
19 | #include "mdp5_kms.h" | |
20 | ||
21 | #include "drm_crtc.h" | |
22 | #include "drm_crtc_helper.h" | |
23 | ||
24 | struct mdp5_encoder { | |
25 | struct drm_encoder base; | |
d145dd78 | 26 | struct mdp5_interface intf; |
0deed25b | 27 | spinlock_t intf_lock; /* protect REG_MDP5_INTF_* registers */ |
06c0dd96 RC |
28 | bool enabled; |
29 | uint32_t bsc; | |
c71716b1 HL |
30 | |
31 | struct mdp5_ctl *ctl; | |
06c0dd96 RC |
32 | }; |
33 | #define to_mdp5_encoder(x) container_of(x, struct mdp5_encoder, base) | |
34 | ||
35 | static struct mdp5_kms *get_kms(struct drm_encoder *encoder) | |
36 | { | |
37 | struct msm_drm_private *priv = encoder->dev->dev_private; | |
38 | return to_mdp5_kms(to_mdp_kms(priv->kms)); | |
39 | } | |
40 | ||
6490ad47 | 41 | #ifdef DOWNSTREAM_CONFIG_MSM_BUS_SCALING |
06c0dd96 RC |
42 | #include <mach/board.h> |
43 | #include <mach/msm_bus.h> | |
44 | #include <mach/msm_bus_board.h> | |
45 | #define MDP_BUS_VECTOR_ENTRY(ab_val, ib_val) \ | |
46 | { \ | |
47 | .src = MSM_BUS_MASTER_MDP_PORT0, \ | |
48 | .dst = MSM_BUS_SLAVE_EBI_CH0, \ | |
49 | .ab = (ab_val), \ | |
50 | .ib = (ib_val), \ | |
51 | } | |
52 | ||
53 | static struct msm_bus_vectors mdp_bus_vectors[] = { | |
54 | MDP_BUS_VECTOR_ENTRY(0, 0), | |
55 | MDP_BUS_VECTOR_ENTRY(2000000000, 2000000000), | |
56 | }; | |
57 | static struct msm_bus_paths mdp_bus_usecases[] = { { | |
58 | .num_paths = 1, | |
59 | .vectors = &mdp_bus_vectors[0], | |
60 | }, { | |
61 | .num_paths = 1, | |
62 | .vectors = &mdp_bus_vectors[1], | |
63 | } }; | |
64 | static struct msm_bus_scale_pdata mdp_bus_scale_table = { | |
65 | .usecase = mdp_bus_usecases, | |
66 | .num_usecases = ARRAY_SIZE(mdp_bus_usecases), | |
67 | .name = "mdss_mdp", | |
68 | }; | |
69 | ||
70 | static void bs_init(struct mdp5_encoder *mdp5_encoder) | |
71 | { | |
72 | mdp5_encoder->bsc = msm_bus_scale_register_client( | |
73 | &mdp_bus_scale_table); | |
74 | DBG("bus scale client: %08x", mdp5_encoder->bsc); | |
75 | } | |
76 | ||
77 | static void bs_fini(struct mdp5_encoder *mdp5_encoder) | |
78 | { | |
79 | if (mdp5_encoder->bsc) { | |
80 | msm_bus_scale_unregister_client(mdp5_encoder->bsc); | |
81 | mdp5_encoder->bsc = 0; | |
82 | } | |
83 | } | |
84 | ||
85 | static void bs_set(struct mdp5_encoder *mdp5_encoder, int idx) | |
86 | { | |
87 | if (mdp5_encoder->bsc) { | |
88 | DBG("set bus scaling: %d", idx); | |
89 | /* HACK: scaling down, and then immediately back up | |
90 | * seems to leave things broken (underflow).. so | |
91 | * never disable: | |
92 | */ | |
93 | idx = 1; | |
94 | msm_bus_scale_client_update_request(mdp5_encoder->bsc, idx); | |
95 | } | |
96 | } | |
97 | #else | |
98 | static void bs_init(struct mdp5_encoder *mdp5_encoder) {} | |
99 | static void bs_fini(struct mdp5_encoder *mdp5_encoder) {} | |
100 | static void bs_set(struct mdp5_encoder *mdp5_encoder, int idx) {} | |
101 | #endif | |
102 | ||
103 | static void mdp5_encoder_destroy(struct drm_encoder *encoder) | |
104 | { | |
105 | struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder); | |
106 | bs_fini(mdp5_encoder); | |
107 | drm_encoder_cleanup(encoder); | |
108 | kfree(mdp5_encoder); | |
109 | } | |
110 | ||
111 | static const struct drm_encoder_funcs mdp5_encoder_funcs = { | |
112 | .destroy = mdp5_encoder_destroy, | |
113 | }; | |
114 | ||
06c0dd96 RC |
115 | static void mdp5_encoder_mode_set(struct drm_encoder *encoder, |
116 | struct drm_display_mode *mode, | |
117 | struct drm_display_mode *adjusted_mode) | |
118 | { | |
119 | struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder); | |
120 | struct mdp5_kms *mdp5_kms = get_kms(encoder); | |
00453981 HL |
121 | struct drm_device *dev = encoder->dev; |
122 | struct drm_connector *connector; | |
d145dd78 | 123 | int intf = mdp5_encoder->intf.num; |
06c0dd96 RC |
124 | uint32_t dtv_hsync_skew, vsync_period, vsync_len, ctrl_pol; |
125 | uint32_t display_v_start, display_v_end; | |
126 | uint32_t hsync_start_x, hsync_end_x; | |
00453981 | 127 | uint32_t format = 0x2100; |
0deed25b | 128 | unsigned long flags; |
06c0dd96 RC |
129 | |
130 | mode = adjusted_mode; | |
131 | ||
132 | DBG("set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x", | |
133 | mode->base.id, mode->name, | |
134 | mode->vrefresh, mode->clock, | |
135 | mode->hdisplay, mode->hsync_start, | |
136 | mode->hsync_end, mode->htotal, | |
137 | mode->vdisplay, mode->vsync_start, | |
138 | mode->vsync_end, mode->vtotal, | |
139 | mode->type, mode->flags); | |
140 | ||
141 | ctrl_pol = 0; | |
1efb92a3 HL |
142 | |
143 | /* DSI controller cannot handle active-low sync signals. */ | |
144 | if (mdp5_encoder->intf.type != INTF_DSI) { | |
145 | if (mode->flags & DRM_MODE_FLAG_NHSYNC) | |
146 | ctrl_pol |= MDP5_INTF_POLARITY_CTL_HSYNC_LOW; | |
147 | if (mode->flags & DRM_MODE_FLAG_NVSYNC) | |
148 | ctrl_pol |= MDP5_INTF_POLARITY_CTL_VSYNC_LOW; | |
149 | } | |
06c0dd96 RC |
150 | /* probably need to get DATA_EN polarity from panel.. */ |
151 | ||
152 | dtv_hsync_skew = 0; /* get this from panel? */ | |
00453981 HL |
153 | |
154 | /* Get color format from panel, default is 8bpc */ | |
155 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { | |
156 | if (connector->encoder == encoder) { | |
157 | switch (connector->display_info.bpc) { | |
158 | case 4: | |
159 | format |= 0; | |
160 | break; | |
161 | case 5: | |
162 | format |= 0x15; | |
163 | break; | |
164 | case 6: | |
165 | format |= 0x2A; | |
166 | break; | |
167 | case 8: | |
168 | default: | |
169 | format |= 0x3F; | |
170 | break; | |
171 | } | |
172 | break; | |
173 | } | |
174 | } | |
06c0dd96 RC |
175 | |
176 | hsync_start_x = (mode->htotal - mode->hsync_start); | |
177 | hsync_end_x = mode->htotal - (mode->hsync_start - mode->hdisplay) - 1; | |
178 | ||
179 | vsync_period = mode->vtotal * mode->htotal; | |
180 | vsync_len = (mode->vsync_end - mode->vsync_start) * mode->htotal; | |
181 | display_v_start = (mode->vtotal - mode->vsync_start) * mode->htotal + dtv_hsync_skew; | |
182 | display_v_end = vsync_period - ((mode->vsync_start - mode->vdisplay) * mode->htotal) + dtv_hsync_skew - 1; | |
183 | ||
00453981 HL |
184 | /* |
185 | * For edp only: | |
186 | * DISPLAY_V_START = (VBP * HCYCLE) + HBP | |
187 | * DISPLAY_V_END = (VBP + VACTIVE) * HCYCLE - 1 - HFP | |
188 | */ | |
d145dd78 | 189 | if (mdp5_encoder->intf.type == INTF_eDP) { |
00453981 HL |
190 | display_v_start += mode->htotal - mode->hsync_start; |
191 | display_v_end -= mode->hsync_start - mode->hdisplay; | |
192 | } | |
193 | ||
0deed25b SV |
194 | spin_lock_irqsave(&mdp5_encoder->intf_lock, flags); |
195 | ||
06c0dd96 RC |
196 | mdp5_write(mdp5_kms, REG_MDP5_INTF_HSYNC_CTL(intf), |
197 | MDP5_INTF_HSYNC_CTL_PULSEW(mode->hsync_end - mode->hsync_start) | | |
198 | MDP5_INTF_HSYNC_CTL_PERIOD(mode->htotal)); | |
199 | mdp5_write(mdp5_kms, REG_MDP5_INTF_VSYNC_PERIOD_F0(intf), vsync_period); | |
200 | mdp5_write(mdp5_kms, REG_MDP5_INTF_VSYNC_LEN_F0(intf), vsync_len); | |
201 | mdp5_write(mdp5_kms, REG_MDP5_INTF_DISPLAY_HCTL(intf), | |
202 | MDP5_INTF_DISPLAY_HCTL_START(hsync_start_x) | | |
203 | MDP5_INTF_DISPLAY_HCTL_END(hsync_end_x)); | |
204 | mdp5_write(mdp5_kms, REG_MDP5_INTF_DISPLAY_VSTART_F0(intf), display_v_start); | |
205 | mdp5_write(mdp5_kms, REG_MDP5_INTF_DISPLAY_VEND_F0(intf), display_v_end); | |
206 | mdp5_write(mdp5_kms, REG_MDP5_INTF_BORDER_COLOR(intf), 0); | |
207 | mdp5_write(mdp5_kms, REG_MDP5_INTF_UNDERFLOW_COLOR(intf), 0xff); | |
208 | mdp5_write(mdp5_kms, REG_MDP5_INTF_HSYNC_SKEW(intf), dtv_hsync_skew); | |
209 | mdp5_write(mdp5_kms, REG_MDP5_INTF_POLARITY_CTL(intf), ctrl_pol); | |
210 | mdp5_write(mdp5_kms, REG_MDP5_INTF_ACTIVE_HCTL(intf), | |
211 | MDP5_INTF_ACTIVE_HCTL_START(0) | | |
212 | MDP5_INTF_ACTIVE_HCTL_END(0)); | |
213 | mdp5_write(mdp5_kms, REG_MDP5_INTF_ACTIVE_VSTART_F0(intf), 0); | |
214 | mdp5_write(mdp5_kms, REG_MDP5_INTF_ACTIVE_VEND_F0(intf), 0); | |
215 | mdp5_write(mdp5_kms, REG_MDP5_INTF_PANEL_FORMAT(intf), format); | |
216 | mdp5_write(mdp5_kms, REG_MDP5_INTF_FRAME_LINE_COUNT_EN(intf), 0x3); /* frame+line? */ | |
0deed25b SV |
217 | |
218 | spin_unlock_irqrestore(&mdp5_encoder->intf_lock, flags); | |
389b09a1 | 219 | |
c71716b1 HL |
220 | mdp5_crtc_set_pipeline(encoder->crtc, &mdp5_encoder->intf, |
221 | mdp5_encoder->ctl); | |
06c0dd96 RC |
222 | } |
223 | ||
0b776d45 | 224 | static void mdp5_encoder_disable(struct drm_encoder *encoder) |
06c0dd96 | 225 | { |
0b776d45 RC |
226 | struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder); |
227 | struct mdp5_kms *mdp5_kms = get_kms(encoder); | |
c71716b1 | 228 | struct mdp5_ctl *ctl = mdp5_encoder->ctl; |
d145dd78 SV |
229 | int lm = mdp5_crtc_get_lm(encoder->crtc); |
230 | struct mdp5_interface *intf = &mdp5_encoder->intf; | |
231 | int intfn = mdp5_encoder->intf.num; | |
0b776d45 RC |
232 | unsigned long flags; |
233 | ||
234 | if (WARN_ON(!mdp5_encoder->enabled)) | |
235 | return; | |
236 | ||
389b09a1 SV |
237 | mdp5_ctl_set_encoder_state(ctl, false); |
238 | ||
0b776d45 | 239 | spin_lock_irqsave(&mdp5_encoder->intf_lock, flags); |
d145dd78 | 240 | mdp5_write(mdp5_kms, REG_MDP5_INTF_TIMING_ENGINE_EN(intfn), 0); |
0b776d45 | 241 | spin_unlock_irqrestore(&mdp5_encoder->intf_lock, flags); |
389b09a1 | 242 | mdp5_ctl_commit(ctl, mdp_ctl_flush_mask_encoder(intf)); |
0b776d45 RC |
243 | |
244 | /* | |
245 | * Wait for a vsync so we know the ENABLE=0 latched before | |
246 | * the (connector) source of the vsync's gets disabled, | |
247 | * otherwise we end up in a funny state if we re-enable | |
248 | * before the disable latches, which results that some of | |
249 | * the settings changes for the new modeset (like new | |
250 | * scanout buffer) don't latch properly.. | |
251 | */ | |
d145dd78 | 252 | mdp_irq_wait(&mdp5_kms->base, intf2vblank(lm, intf)); |
0b776d45 RC |
253 | |
254 | bs_set(mdp5_encoder, 0); | |
255 | ||
256 | mdp5_encoder->enabled = false; | |
06c0dd96 RC |
257 | } |
258 | ||
0b776d45 | 259 | static void mdp5_encoder_enable(struct drm_encoder *encoder) |
06c0dd96 RC |
260 | { |
261 | struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder); | |
0b776d45 | 262 | struct mdp5_kms *mdp5_kms = get_kms(encoder); |
c71716b1 | 263 | struct mdp5_ctl *ctl = mdp5_encoder->ctl; |
389b09a1 | 264 | struct mdp5_interface *intf = &mdp5_encoder->intf; |
d145dd78 | 265 | int intfn = mdp5_encoder->intf.num; |
0b776d45 RC |
266 | unsigned long flags; |
267 | ||
268 | if (WARN_ON(mdp5_encoder->enabled)) | |
269 | return; | |
270 | ||
0b776d45 RC |
271 | bs_set(mdp5_encoder, 1); |
272 | spin_lock_irqsave(&mdp5_encoder->intf_lock, flags); | |
d145dd78 | 273 | mdp5_write(mdp5_kms, REG_MDP5_INTF_TIMING_ENGINE_EN(intfn), 1); |
0b776d45 | 274 | spin_unlock_irqrestore(&mdp5_encoder->intf_lock, flags); |
389b09a1 SV |
275 | mdp5_ctl_commit(ctl, mdp_ctl_flush_mask_encoder(intf)); |
276 | ||
277 | mdp5_ctl_set_encoder_state(ctl, true); | |
0b776d45 | 278 | |
5db0f6e8 | 279 | mdp5_encoder->enabled = true; |
06c0dd96 RC |
280 | } |
281 | ||
282 | static const struct drm_encoder_helper_funcs mdp5_encoder_helper_funcs = { | |
06c0dd96 | 283 | .mode_set = mdp5_encoder_mode_set, |
5db0f6e8 SV |
284 | .disable = mdp5_encoder_disable, |
285 | .enable = mdp5_encoder_enable, | |
06c0dd96 RC |
286 | }; |
287 | ||
e2dd9f9f AT |
288 | int mdp5_encoder_get_linecount(struct drm_encoder *encoder) |
289 | { | |
290 | struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder); | |
291 | struct mdp5_kms *mdp5_kms = get_kms(encoder); | |
292 | int intf = mdp5_encoder->intf.num; | |
293 | ||
294 | return mdp5_read(mdp5_kms, REG_MDP5_INTF_LINE_COUNT(intf)); | |
295 | } | |
296 | ||
297 | u32 mdp5_encoder_get_framecount(struct drm_encoder *encoder) | |
298 | { | |
299 | struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder); | |
300 | struct mdp5_kms *mdp5_kms = get_kms(encoder); | |
301 | int intf = mdp5_encoder->intf.num; | |
302 | ||
303 | return mdp5_read(mdp5_kms, REG_MDP5_INTF_FRAME_COUNT(intf)); | |
304 | } | |
305 | ||
d5af49c9 HL |
306 | int mdp5_encoder_set_split_display(struct drm_encoder *encoder, |
307 | struct drm_encoder *slave_encoder) | |
308 | { | |
309 | struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder); | |
b96b3a06 | 310 | struct mdp5_encoder *mdp5_slave_enc = to_mdp5_encoder(slave_encoder); |
d5af49c9 HL |
311 | struct mdp5_kms *mdp5_kms; |
312 | int intf_num; | |
313 | u32 data = 0; | |
314 | ||
315 | if (!encoder || !slave_encoder) | |
316 | return -EINVAL; | |
317 | ||
318 | mdp5_kms = get_kms(encoder); | |
319 | intf_num = mdp5_encoder->intf.num; | |
320 | ||
321 | /* Switch slave encoder's TimingGen Sync mode, | |
322 | * to use the master's enable signal for the slave encoder. | |
323 | */ | |
324 | if (intf_num == 1) | |
7b59c7e4 | 325 | data |= MDP5_SPLIT_DPL_LOWER_INTF2_TG_SYNC; |
d5af49c9 | 326 | else if (intf_num == 2) |
7b59c7e4 | 327 | data |= MDP5_SPLIT_DPL_LOWER_INTF1_TG_SYNC; |
d5af49c9 HL |
328 | else |
329 | return -EINVAL; | |
330 | ||
331 | /* Make sure clocks are on when connectors calling this function. */ | |
332 | mdp5_enable(mdp5_kms); | |
d5af49c9 | 333 | /* Dumb Panel, Sync mode */ |
7b59c7e4 AT |
334 | mdp5_write(mdp5_kms, REG_MDP5_SPLIT_DPL_UPPER, 0); |
335 | mdp5_write(mdp5_kms, REG_MDP5_SPLIT_DPL_LOWER, data); | |
336 | mdp5_write(mdp5_kms, REG_MDP5_SPLIT_DPL_EN, 1); | |
b96b3a06 HL |
337 | |
338 | mdp5_ctl_pair(mdp5_encoder->ctl, mdp5_slave_enc->ctl, true); | |
339 | ||
d5af49c9 HL |
340 | mdp5_disable(mdp5_kms); |
341 | ||
342 | return 0; | |
343 | } | |
344 | ||
9c9f6f8d AT |
345 | void mdp5_encoder_set_intf_mode(struct drm_encoder *encoder, bool cmd_mode) |
346 | { | |
347 | struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder); | |
348 | struct mdp5_interface *intf = &mdp5_encoder->intf; | |
349 | ||
350 | /* TODO: Expand this to set writeback modes too */ | |
351 | if (cmd_mode) { | |
352 | WARN_ON(intf->type != INTF_DSI); | |
353 | intf->mode = MDP5_INTF_DSI_MODE_COMMAND; | |
354 | } else { | |
355 | if (intf->type == INTF_DSI) | |
356 | intf->mode = MDP5_INTF_DSI_MODE_VIDEO; | |
357 | else | |
358 | intf->mode = MDP5_INTF_MODE_NONE; | |
359 | } | |
360 | } | |
361 | ||
06c0dd96 | 362 | /* initialize encoder */ |
d145dd78 | 363 | struct drm_encoder *mdp5_encoder_init(struct drm_device *dev, |
c71716b1 | 364 | struct mdp5_interface *intf, struct mdp5_ctl *ctl) |
06c0dd96 RC |
365 | { |
366 | struct drm_encoder *encoder = NULL; | |
367 | struct mdp5_encoder *mdp5_encoder; | |
d5af49c9 HL |
368 | int enc_type = (intf->type == INTF_DSI) ? |
369 | DRM_MODE_ENCODER_DSI : DRM_MODE_ENCODER_TMDS; | |
06c0dd96 RC |
370 | int ret; |
371 | ||
372 | mdp5_encoder = kzalloc(sizeof(*mdp5_encoder), GFP_KERNEL); | |
373 | if (!mdp5_encoder) { | |
374 | ret = -ENOMEM; | |
375 | goto fail; | |
376 | } | |
377 | ||
d145dd78 | 378 | memcpy(&mdp5_encoder->intf, intf, sizeof(mdp5_encoder->intf)); |
06c0dd96 | 379 | encoder = &mdp5_encoder->base; |
c71716b1 | 380 | mdp5_encoder->ctl = ctl; |
06c0dd96 | 381 | |
0deed25b SV |
382 | spin_lock_init(&mdp5_encoder->intf_lock); |
383 | ||
13a3d91f | 384 | drm_encoder_init(dev, encoder, &mdp5_encoder_funcs, enc_type, NULL); |
d5af49c9 | 385 | |
06c0dd96 RC |
386 | drm_encoder_helper_add(encoder, &mdp5_encoder_helper_funcs); |
387 | ||
388 | bs_init(mdp5_encoder); | |
389 | ||
390 | return encoder; | |
391 | ||
392 | fail: | |
393 | if (encoder) | |
394 | mdp5_encoder_destroy(encoder); | |
395 | ||
396 | return ERR_PTR(ret); | |
397 | } |