]>
Commit | Line | Data |
---|---|---|
06c0dd96 RC |
1 | /* |
2 | * Copyright (C) 2013 Red Hat | |
3 | * Author: Rob Clark <robdclark@gmail.com> | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify it | |
6 | * under the terms of the GNU General Public License version 2 as published by | |
7 | * the Free Software Foundation. | |
8 | * | |
9 | * This program is distributed in the hope that it will be useful, but WITHOUT | |
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
12 | * more details. | |
13 | * | |
14 | * You should have received a copy of the GNU General Public License along with | |
15 | * this program. If not, see <http://www.gnu.org/licenses/>. | |
16 | */ | |
17 | ||
18 | #include "mdp5_kms.h" | |
19 | ||
20 | #include <drm/drm_mode.h> | |
21 | #include "drm_crtc.h" | |
22 | #include "drm_crtc_helper.h" | |
23 | #include "drm_flip_work.h" | |
24 | ||
25 | struct mdp5_crtc { | |
26 | struct drm_crtc base; | |
27 | char name[8]; | |
28 | struct drm_plane *plane; | |
29 | struct drm_plane *planes[8]; | |
30 | int id; | |
31 | bool enabled; | |
32 | ||
33 | /* which mixer/encoder we route output to: */ | |
34 | int mixer; | |
35 | ||
36 | /* if there is a pending flip, these will be non-null: */ | |
37 | struct drm_pending_vblank_event *event; | |
38 | struct msm_fence_cb pageflip_cb; | |
39 | ||
40 | #define PENDING_CURSOR 0x1 | |
41 | #define PENDING_FLIP 0x2 | |
42 | atomic_t pending; | |
43 | ||
44 | /* the fb that we logically (from PoV of KMS API) hold a ref | |
45 | * to. Which we may not yet be scanning out (we may still | |
46 | * be scanning out previous in case of page_flip while waiting | |
47 | * for gpu rendering to complete: | |
48 | */ | |
49 | struct drm_framebuffer *fb; | |
50 | ||
51 | /* the fb that we currently hold a scanout ref to: */ | |
52 | struct drm_framebuffer *scanout_fb; | |
53 | ||
54 | /* for unref'ing framebuffers after scanout completes: */ | |
55 | struct drm_flip_work unref_fb_work; | |
56 | ||
57 | struct mdp_irq vblank; | |
58 | struct mdp_irq err; | |
59 | }; | |
60 | #define to_mdp5_crtc(x) container_of(x, struct mdp5_crtc, base) | |
61 | ||
62 | static struct mdp5_kms *get_kms(struct drm_crtc *crtc) | |
63 | { | |
64 | struct msm_drm_private *priv = crtc->dev->dev_private; | |
65 | return to_mdp5_kms(to_mdp_kms(priv->kms)); | |
66 | } | |
67 | ||
68 | static void request_pending(struct drm_crtc *crtc, uint32_t pending) | |
69 | { | |
70 | struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); | |
71 | ||
72 | atomic_or(pending, &mdp5_crtc->pending); | |
73 | mdp_irq_register(&get_kms(crtc)->base, &mdp5_crtc->vblank); | |
74 | } | |
75 | ||
76 | static void crtc_flush(struct drm_crtc *crtc) | |
77 | { | |
78 | struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); | |
79 | struct mdp5_kms *mdp5_kms = get_kms(crtc); | |
80 | int id = mdp5_crtc->id; | |
81 | uint32_t i, flush = 0; | |
82 | ||
83 | for (i = 0; i < ARRAY_SIZE(mdp5_crtc->planes); i++) { | |
84 | struct drm_plane *plane = mdp5_crtc->planes[i]; | |
85 | if (plane) { | |
86 | enum mdp5_pipe pipe = mdp5_plane_pipe(plane); | |
87 | flush |= pipe2flush(pipe); | |
88 | } | |
89 | } | |
90 | flush |= mixer2flush(mdp5_crtc->id); | |
91 | flush |= MDP5_CTL_FLUSH_CTL; | |
92 | ||
93 | DBG("%s: flush=%08x", mdp5_crtc->name, flush); | |
94 | ||
95 | mdp5_write(mdp5_kms, REG_MDP5_CTL_FLUSH(id), flush); | |
96 | } | |
97 | ||
98 | static void update_fb(struct drm_crtc *crtc, struct drm_framebuffer *new_fb) | |
99 | { | |
100 | struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); | |
101 | struct drm_framebuffer *old_fb = mdp5_crtc->fb; | |
102 | ||
103 | /* grab reference to incoming scanout fb: */ | |
104 | drm_framebuffer_reference(new_fb); | |
105 | mdp5_crtc->base.fb = new_fb; | |
106 | mdp5_crtc->fb = new_fb; | |
107 | ||
108 | if (old_fb) | |
109 | drm_flip_work_queue(&mdp5_crtc->unref_fb_work, old_fb); | |
110 | } | |
111 | ||
112 | /* unlike update_fb(), take a ref to the new scanout fb *before* updating | |
113 | * plane, then call this. Needed to ensure we don't unref the buffer that | |
114 | * is actually still being scanned out. | |
115 | * | |
116 | * Note that this whole thing goes away with atomic.. since we can defer | |
117 | * calling into driver until rendering is done. | |
118 | */ | |
119 | static void update_scanout(struct drm_crtc *crtc, struct drm_framebuffer *fb) | |
120 | { | |
121 | struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); | |
122 | ||
123 | /* flush updates, to make sure hw is updated to new scanout fb, | |
124 | * so that we can safely queue unref to current fb (ie. next | |
125 | * vblank we know hw is done w/ previous scanout_fb). | |
126 | */ | |
127 | crtc_flush(crtc); | |
128 | ||
129 | if (mdp5_crtc->scanout_fb) | |
130 | drm_flip_work_queue(&mdp5_crtc->unref_fb_work, | |
131 | mdp5_crtc->scanout_fb); | |
132 | ||
133 | mdp5_crtc->scanout_fb = fb; | |
134 | ||
135 | /* enable vblank to complete flip: */ | |
136 | request_pending(crtc, PENDING_FLIP); | |
137 | } | |
138 | ||
139 | /* if file!=NULL, this is preclose potential cancel-flip path */ | |
140 | static void complete_flip(struct drm_crtc *crtc, struct drm_file *file) | |
141 | { | |
142 | struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); | |
143 | struct drm_device *dev = crtc->dev; | |
144 | struct drm_pending_vblank_event *event; | |
145 | unsigned long flags, i; | |
146 | ||
147 | spin_lock_irqsave(&dev->event_lock, flags); | |
148 | event = mdp5_crtc->event; | |
149 | if (event) { | |
150 | /* if regular vblank case (!file) or if cancel-flip from | |
151 | * preclose on file that requested flip, then send the | |
152 | * event: | |
153 | */ | |
154 | if (!file || (event->base.file_priv == file)) { | |
155 | mdp5_crtc->event = NULL; | |
156 | drm_send_vblank_event(dev, mdp5_crtc->id, event); | |
157 | } | |
158 | } | |
159 | spin_unlock_irqrestore(&dev->event_lock, flags); | |
160 | ||
161 | for (i = 0; i < ARRAY_SIZE(mdp5_crtc->planes); i++) { | |
162 | struct drm_plane *plane = mdp5_crtc->planes[i]; | |
163 | if (plane) | |
164 | mdp5_plane_complete_flip(plane); | |
165 | } | |
166 | } | |
167 | ||
168 | static void pageflip_cb(struct msm_fence_cb *cb) | |
169 | { | |
170 | struct mdp5_crtc *mdp5_crtc = | |
171 | container_of(cb, struct mdp5_crtc, pageflip_cb); | |
172 | struct drm_crtc *crtc = &mdp5_crtc->base; | |
173 | struct drm_framebuffer *fb = mdp5_crtc->fb; | |
174 | ||
175 | if (!fb) | |
176 | return; | |
177 | ||
178 | drm_framebuffer_reference(fb); | |
179 | mdp5_plane_set_scanout(mdp5_crtc->plane, fb); | |
180 | update_scanout(crtc, fb); | |
181 | } | |
182 | ||
183 | static void unref_fb_worker(struct drm_flip_work *work, void *val) | |
184 | { | |
185 | struct mdp5_crtc *mdp5_crtc = | |
186 | container_of(work, struct mdp5_crtc, unref_fb_work); | |
187 | struct drm_device *dev = mdp5_crtc->base.dev; | |
188 | ||
189 | mutex_lock(&dev->mode_config.mutex); | |
190 | drm_framebuffer_unreference(val); | |
191 | mutex_unlock(&dev->mode_config.mutex); | |
192 | } | |
193 | ||
194 | static void mdp5_crtc_destroy(struct drm_crtc *crtc) | |
195 | { | |
196 | struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); | |
197 | ||
198 | mdp5_crtc->plane->funcs->destroy(mdp5_crtc->plane); | |
199 | ||
200 | drm_crtc_cleanup(crtc); | |
201 | drm_flip_work_cleanup(&mdp5_crtc->unref_fb_work); | |
202 | ||
203 | kfree(mdp5_crtc); | |
204 | } | |
205 | ||
206 | static void mdp5_crtc_dpms(struct drm_crtc *crtc, int mode) | |
207 | { | |
208 | struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); | |
209 | struct mdp5_kms *mdp5_kms = get_kms(crtc); | |
210 | bool enabled = (mode == DRM_MODE_DPMS_ON); | |
211 | ||
212 | DBG("%s: mode=%d", mdp5_crtc->name, mode); | |
213 | ||
214 | if (enabled != mdp5_crtc->enabled) { | |
215 | if (enabled) { | |
216 | mdp5_enable(mdp5_kms); | |
217 | mdp_irq_register(&mdp5_kms->base, &mdp5_crtc->err); | |
218 | } else { | |
219 | mdp_irq_unregister(&mdp5_kms->base, &mdp5_crtc->err); | |
220 | mdp5_disable(mdp5_kms); | |
221 | } | |
222 | mdp5_crtc->enabled = enabled; | |
223 | } | |
224 | } | |
225 | ||
226 | static bool mdp5_crtc_mode_fixup(struct drm_crtc *crtc, | |
227 | const struct drm_display_mode *mode, | |
228 | struct drm_display_mode *adjusted_mode) | |
229 | { | |
230 | return true; | |
231 | } | |
232 | ||
233 | static void blend_setup(struct drm_crtc *crtc) | |
234 | { | |
235 | struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); | |
236 | struct mdp5_kms *mdp5_kms = get_kms(crtc); | |
237 | int id = mdp5_crtc->id; | |
238 | ||
239 | /* | |
240 | * Hard-coded setup for now until I figure out how the | |
241 | * layer-mixer works | |
242 | */ | |
243 | ||
244 | /* LM[id]: */ | |
245 | mdp5_write(mdp5_kms, REG_MDP5_LM_BLEND_COLOR_OUT(id), | |
246 | MDP5_LM_BLEND_COLOR_OUT_STAGE0_FG_ALPHA); | |
247 | mdp5_write(mdp5_kms, REG_MDP5_LM_BLEND_OP_MODE(id, 0), | |
248 | MDP5_LM_BLEND_OP_MODE_FG_ALPHA(FG_CONST) | | |
249 | MDP5_LM_BLEND_OP_MODE_BG_ALPHA(FG_PIXEL) | | |
250 | MDP5_LM_BLEND_OP_MODE_BG_INV_ALPHA); | |
251 | mdp5_write(mdp5_kms, REG_MDP5_LM_BLEND_FG_ALPHA(id, 0), 0xff); | |
252 | mdp5_write(mdp5_kms, REG_MDP5_LM_BLEND_BG_ALPHA(id, 0), 0x00); | |
253 | ||
254 | /* NOTE: seems that LM[n] and CTL[m], we do not need n==m.. but | |
255 | * we want to be setting CTL[m].LAYER[n]. Not sure what the | |
256 | * point of having CTL[m].LAYER[o] (for o!=n).. maybe that is | |
257 | * used when chaining up mixers for high resolution displays? | |
258 | */ | |
259 | ||
260 | /* CTL[id]: */ | |
261 | mdp5_write(mdp5_kms, REG_MDP5_CTL_LAYER_REG(id, 0), | |
262 | MDP5_CTL_LAYER_REG_RGB0(STAGE0) | | |
263 | MDP5_CTL_LAYER_REG_BORDER_COLOR); | |
264 | mdp5_write(mdp5_kms, REG_MDP5_CTL_LAYER_REG(id, 1), 0); | |
265 | mdp5_write(mdp5_kms, REG_MDP5_CTL_LAYER_REG(id, 2), 0); | |
266 | mdp5_write(mdp5_kms, REG_MDP5_CTL_LAYER_REG(id, 3), 0); | |
267 | mdp5_write(mdp5_kms, REG_MDP5_CTL_LAYER_REG(id, 4), 0); | |
268 | } | |
269 | ||
270 | static int mdp5_crtc_mode_set(struct drm_crtc *crtc, | |
271 | struct drm_display_mode *mode, | |
272 | struct drm_display_mode *adjusted_mode, | |
273 | int x, int y, | |
274 | struct drm_framebuffer *old_fb) | |
275 | { | |
276 | struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); | |
277 | struct mdp5_kms *mdp5_kms = get_kms(crtc); | |
278 | int ret; | |
279 | ||
280 | mode = adjusted_mode; | |
281 | ||
282 | DBG("%s: set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x", | |
283 | mdp5_crtc->name, mode->base.id, mode->name, | |
284 | mode->vrefresh, mode->clock, | |
285 | mode->hdisplay, mode->hsync_start, | |
286 | mode->hsync_end, mode->htotal, | |
287 | mode->vdisplay, mode->vsync_start, | |
288 | mode->vsync_end, mode->vtotal, | |
289 | mode->type, mode->flags); | |
290 | ||
291 | /* grab extra ref for update_scanout() */ | |
292 | drm_framebuffer_reference(crtc->fb); | |
293 | ||
294 | ret = mdp5_plane_mode_set(mdp5_crtc->plane, crtc, crtc->fb, | |
295 | 0, 0, mode->hdisplay, mode->vdisplay, | |
296 | x << 16, y << 16, | |
297 | mode->hdisplay << 16, mode->vdisplay << 16); | |
298 | if (ret) { | |
37033a76 | 299 | drm_framebuffer_unreference(crtc->fb); |
06c0dd96 RC |
300 | dev_err(crtc->dev->dev, "%s: failed to set mode on plane: %d\n", |
301 | mdp5_crtc->name, ret); | |
302 | return ret; | |
303 | } | |
304 | ||
305 | mdp5_write(mdp5_kms, REG_MDP5_LM_OUT_SIZE(mdp5_crtc->id), | |
306 | MDP5_LM_OUT_SIZE_WIDTH(mode->hdisplay) | | |
307 | MDP5_LM_OUT_SIZE_HEIGHT(mode->vdisplay)); | |
308 | ||
309 | update_fb(crtc, crtc->fb); | |
310 | update_scanout(crtc, crtc->fb); | |
311 | ||
312 | return 0; | |
313 | } | |
314 | ||
315 | static void mdp5_crtc_prepare(struct drm_crtc *crtc) | |
316 | { | |
317 | struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); | |
318 | DBG("%s", mdp5_crtc->name); | |
319 | /* make sure we hold a ref to mdp clks while setting up mode: */ | |
320 | mdp5_enable(get_kms(crtc)); | |
321 | mdp5_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); | |
322 | } | |
323 | ||
324 | static void mdp5_crtc_commit(struct drm_crtc *crtc) | |
325 | { | |
326 | mdp5_crtc_dpms(crtc, DRM_MODE_DPMS_ON); | |
327 | crtc_flush(crtc); | |
328 | /* drop the ref to mdp clk's that we got in prepare: */ | |
329 | mdp5_disable(get_kms(crtc)); | |
330 | } | |
331 | ||
332 | static int mdp5_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, | |
333 | struct drm_framebuffer *old_fb) | |
334 | { | |
335 | struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); | |
336 | struct drm_plane *plane = mdp5_crtc->plane; | |
337 | struct drm_display_mode *mode = &crtc->mode; | |
338 | int ret; | |
339 | ||
340 | /* grab extra ref for update_scanout() */ | |
341 | drm_framebuffer_reference(crtc->fb); | |
342 | ||
343 | ret = mdp5_plane_mode_set(plane, crtc, crtc->fb, | |
344 | 0, 0, mode->hdisplay, mode->vdisplay, | |
345 | x << 16, y << 16, | |
346 | mode->hdisplay << 16, mode->vdisplay << 16); | |
37033a76 RC |
347 | if (ret) { |
348 | drm_framebuffer_unreference(crtc->fb); | |
349 | return ret; | |
350 | } | |
06c0dd96 RC |
351 | |
352 | update_fb(crtc, crtc->fb); | |
353 | update_scanout(crtc, crtc->fb); | |
354 | ||
37033a76 | 355 | return 0; |
06c0dd96 RC |
356 | } |
357 | ||
358 | static void mdp5_crtc_load_lut(struct drm_crtc *crtc) | |
359 | { | |
360 | } | |
361 | ||
362 | static int mdp5_crtc_page_flip(struct drm_crtc *crtc, | |
363 | struct drm_framebuffer *new_fb, | |
364 | struct drm_pending_vblank_event *event, | |
365 | uint32_t page_flip_flags) | |
366 | { | |
367 | struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); | |
368 | struct drm_device *dev = crtc->dev; | |
369 | struct drm_gem_object *obj; | |
370 | unsigned long flags; | |
371 | ||
372 | if (mdp5_crtc->event) { | |
373 | dev_err(dev->dev, "already pending flip!\n"); | |
374 | return -EBUSY; | |
375 | } | |
376 | ||
377 | obj = msm_framebuffer_bo(new_fb, 0); | |
378 | ||
379 | spin_lock_irqsave(&dev->event_lock, flags); | |
380 | mdp5_crtc->event = event; | |
381 | spin_unlock_irqrestore(&dev->event_lock, flags); | |
382 | ||
383 | update_fb(crtc, new_fb); | |
384 | ||
385 | return msm_gem_queue_inactive_cb(obj, &mdp5_crtc->pageflip_cb); | |
386 | } | |
387 | ||
388 | static int mdp5_crtc_set_property(struct drm_crtc *crtc, | |
389 | struct drm_property *property, uint64_t val) | |
390 | { | |
391 | // XXX | |
392 | return -EINVAL; | |
393 | } | |
394 | ||
395 | static const struct drm_crtc_funcs mdp5_crtc_funcs = { | |
396 | .set_config = drm_crtc_helper_set_config, | |
397 | .destroy = mdp5_crtc_destroy, | |
398 | .page_flip = mdp5_crtc_page_flip, | |
399 | .set_property = mdp5_crtc_set_property, | |
400 | }; | |
401 | ||
402 | static const struct drm_crtc_helper_funcs mdp5_crtc_helper_funcs = { | |
403 | .dpms = mdp5_crtc_dpms, | |
404 | .mode_fixup = mdp5_crtc_mode_fixup, | |
405 | .mode_set = mdp5_crtc_mode_set, | |
406 | .prepare = mdp5_crtc_prepare, | |
407 | .commit = mdp5_crtc_commit, | |
408 | .mode_set_base = mdp5_crtc_mode_set_base, | |
409 | .load_lut = mdp5_crtc_load_lut, | |
410 | }; | |
411 | ||
412 | static void mdp5_crtc_vblank_irq(struct mdp_irq *irq, uint32_t irqstatus) | |
413 | { | |
414 | struct mdp5_crtc *mdp5_crtc = container_of(irq, struct mdp5_crtc, vblank); | |
415 | struct drm_crtc *crtc = &mdp5_crtc->base; | |
416 | struct msm_drm_private *priv = crtc->dev->dev_private; | |
417 | unsigned pending; | |
418 | ||
419 | mdp_irq_unregister(&get_kms(crtc)->base, &mdp5_crtc->vblank); | |
420 | ||
421 | pending = atomic_xchg(&mdp5_crtc->pending, 0); | |
422 | ||
423 | if (pending & PENDING_FLIP) { | |
424 | complete_flip(crtc, NULL); | |
425 | drm_flip_work_commit(&mdp5_crtc->unref_fb_work, priv->wq); | |
426 | } | |
427 | } | |
428 | ||
429 | static void mdp5_crtc_err_irq(struct mdp_irq *irq, uint32_t irqstatus) | |
430 | { | |
431 | struct mdp5_crtc *mdp5_crtc = container_of(irq, struct mdp5_crtc, err); | |
432 | struct drm_crtc *crtc = &mdp5_crtc->base; | |
433 | DBG("%s: error: %08x", mdp5_crtc->name, irqstatus); | |
434 | crtc_flush(crtc); | |
435 | } | |
436 | ||
437 | uint32_t mdp5_crtc_vblank(struct drm_crtc *crtc) | |
438 | { | |
439 | struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); | |
440 | return mdp5_crtc->vblank.irqmask; | |
441 | } | |
442 | ||
443 | void mdp5_crtc_cancel_pending_flip(struct drm_crtc *crtc, struct drm_file *file) | |
444 | { | |
445 | DBG("cancel: %p", file); | |
446 | complete_flip(crtc, file); | |
447 | } | |
448 | ||
449 | /* set interface for routing crtc->encoder: */ | |
450 | void mdp5_crtc_set_intf(struct drm_crtc *crtc, int intf, | |
451 | enum mdp5_intf intf_id) | |
452 | { | |
453 | struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); | |
454 | struct mdp5_kms *mdp5_kms = get_kms(crtc); | |
455 | static const enum mdp5_intfnum intfnum[] = { | |
456 | INTF0, INTF1, INTF2, INTF3, | |
457 | }; | |
458 | uint32_t intf_sel; | |
459 | ||
460 | /* now that we know what irq's we want: */ | |
461 | mdp5_crtc->err.irqmask = intf2err(intf); | |
462 | mdp5_crtc->vblank.irqmask = intf2vblank(intf); | |
463 | ||
464 | /* when called from modeset_init(), skip the rest until later: */ | |
465 | if (!mdp5_kms) | |
466 | return; | |
467 | ||
468 | intf_sel = mdp5_read(mdp5_kms, REG_MDP5_DISP_INTF_SEL); | |
469 | ||
470 | switch (intf) { | |
471 | case 0: | |
472 | intf_sel &= ~MDP5_DISP_INTF_SEL_INTF0__MASK; | |
473 | intf_sel |= MDP5_DISP_INTF_SEL_INTF0(intf_id); | |
474 | break; | |
475 | case 1: | |
476 | intf_sel &= ~MDP5_DISP_INTF_SEL_INTF1__MASK; | |
477 | intf_sel |= MDP5_DISP_INTF_SEL_INTF1(intf_id); | |
478 | break; | |
479 | case 2: | |
480 | intf_sel &= ~MDP5_DISP_INTF_SEL_INTF2__MASK; | |
481 | intf_sel |= MDP5_DISP_INTF_SEL_INTF2(intf_id); | |
482 | break; | |
483 | case 3: | |
484 | intf_sel &= ~MDP5_DISP_INTF_SEL_INTF3__MASK; | |
485 | intf_sel |= MDP5_DISP_INTF_SEL_INTF3(intf_id); | |
486 | break; | |
487 | default: | |
488 | BUG(); | |
489 | break; | |
490 | } | |
491 | ||
492 | blend_setup(crtc); | |
493 | ||
494 | DBG("%s: intf_sel=%08x", mdp5_crtc->name, intf_sel); | |
495 | ||
496 | mdp5_write(mdp5_kms, REG_MDP5_DISP_INTF_SEL, intf_sel); | |
497 | mdp5_write(mdp5_kms, REG_MDP5_CTL_OP(mdp5_crtc->id), | |
498 | MDP5_CTL_OP_MODE(MODE_NONE) | | |
499 | MDP5_CTL_OP_INTF_NUM(intfnum[intf])); | |
500 | ||
501 | crtc_flush(crtc); | |
502 | } | |
503 | ||
504 | static void set_attach(struct drm_crtc *crtc, enum mdp5_pipe pipe_id, | |
505 | struct drm_plane *plane) | |
506 | { | |
507 | struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); | |
508 | ||
509 | BUG_ON(pipe_id >= ARRAY_SIZE(mdp5_crtc->planes)); | |
510 | ||
511 | if (mdp5_crtc->planes[pipe_id] == plane) | |
512 | return; | |
513 | ||
514 | mdp5_crtc->planes[pipe_id] = plane; | |
515 | blend_setup(crtc); | |
516 | if (mdp5_crtc->enabled && (plane != mdp5_crtc->plane)) | |
517 | crtc_flush(crtc); | |
518 | } | |
519 | ||
520 | void mdp5_crtc_attach(struct drm_crtc *crtc, struct drm_plane *plane) | |
521 | { | |
522 | set_attach(crtc, mdp5_plane_pipe(plane), plane); | |
523 | } | |
524 | ||
525 | void mdp5_crtc_detach(struct drm_crtc *crtc, struct drm_plane *plane) | |
526 | { | |
527 | set_attach(crtc, mdp5_plane_pipe(plane), NULL); | |
528 | } | |
529 | ||
530 | /* initialize crtc */ | |
531 | struct drm_crtc *mdp5_crtc_init(struct drm_device *dev, | |
532 | struct drm_plane *plane, int id) | |
533 | { | |
534 | struct drm_crtc *crtc = NULL; | |
535 | struct mdp5_crtc *mdp5_crtc; | |
536 | int ret; | |
537 | ||
538 | mdp5_crtc = kzalloc(sizeof(*mdp5_crtc), GFP_KERNEL); | |
539 | if (!mdp5_crtc) { | |
540 | ret = -ENOMEM; | |
541 | goto fail; | |
542 | } | |
543 | ||
544 | crtc = &mdp5_crtc->base; | |
545 | ||
546 | mdp5_crtc->plane = plane; | |
547 | mdp5_crtc->id = id; | |
548 | ||
549 | mdp5_crtc->vblank.irq = mdp5_crtc_vblank_irq; | |
550 | mdp5_crtc->err.irq = mdp5_crtc_err_irq; | |
551 | ||
552 | snprintf(mdp5_crtc->name, sizeof(mdp5_crtc->name), "%s:%d", | |
553 | pipe2name(mdp5_plane_pipe(plane)), id); | |
554 | ||
555 | ret = drm_flip_work_init(&mdp5_crtc->unref_fb_work, 16, | |
556 | "unref fb", unref_fb_worker); | |
557 | if (ret) | |
558 | goto fail; | |
559 | ||
560 | INIT_FENCE_CB(&mdp5_crtc->pageflip_cb, pageflip_cb); | |
561 | ||
562 | drm_crtc_init(dev, crtc, &mdp5_crtc_funcs); | |
563 | drm_crtc_helper_add(crtc, &mdp5_crtc_helper_funcs); | |
564 | ||
565 | mdp5_plane_install_properties(mdp5_crtc->plane, &crtc->base); | |
566 | ||
567 | return crtc; | |
568 | ||
569 | fail: | |
570 | if (crtc) | |
571 | mdp5_crtc_destroy(crtc); | |
572 | ||
573 | return ERR_PTR(ret); | |
574 | } |