Commit | Line | Data |
---|---|---|
cd5351f4 | 1 | /* |
8bb0daff | 2 | * drivers/gpu/drm/omapdrm/omap_crtc.c |
cd5351f4 RC |
3 | * |
4 | * Copyright (C) 2011 Texas Instruments | |
5 | * Author: Rob Clark <rob@ti.com> | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify it | |
8 | * under the terms of the GNU General Public License version 2 as published by | |
9 | * the Free Software Foundation. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, but WITHOUT | |
12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
14 | * more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License along with | |
17 | * this program. If not, see <http://www.gnu.org/licenses/>. | |
18 | */ | |
19 | ||
a42133a7 LP |
20 | #include <linux/completion.h> |
21 | ||
69a12263 LP |
22 | #include <drm/drm_atomic.h> |
23 | #include <drm/drm_atomic_helper.h> | |
2d278f54 LP |
24 | #include <drm/drm_crtc.h> |
25 | #include <drm/drm_crtc_helper.h> | |
b9ed9f0e | 26 | #include <drm/drm_mode.h> |
3cb9ae4f | 27 | #include <drm/drm_plane_helper.h> |
2d278f54 LP |
28 | |
29 | #include "omap_drv.h" | |
cd5351f4 RC |
30 | |
31 | #define to_omap_crtc(x) container_of(x, struct omap_crtc, base) | |
32 | ||
15d02e92 LP |
33 | enum omap_page_flip_state { |
34 | OMAP_PAGE_FLIP_IDLE, | |
35 | OMAP_PAGE_FLIP_WAIT, | |
36 | OMAP_PAGE_FLIP_QUEUED, | |
37 | OMAP_PAGE_FLIP_CANCELLED, | |
38 | }; | |
39 | ||
cd5351f4 RC |
40 | struct omap_crtc { |
41 | struct drm_crtc base; | |
f5f9454c | 42 | |
bb5c2d9a | 43 | const char *name; |
f5f9454c RC |
44 | int pipe; |
45 | enum omap_channel channel; | |
46 | struct omap_overlay_manager_info info; | |
c7aef12f | 47 | struct drm_encoder *current_encoder; |
f5f9454c RC |
48 | |
49 | /* | |
50 | * Temporary: eventually this will go away, but it is needed | |
51 | * for now to keep the output's happy. (They only need | |
52 | * mgr->id.) Eventually this will be replaced w/ something | |
53 | * more common-panel-framework-y | |
54 | */ | |
04b1fc02 | 55 | struct omap_overlay_manager *mgr; |
f5f9454c RC |
56 | |
57 | struct omap_video_timings timings; | |
58 | bool enabled; | |
f5f9454c | 59 | |
a42133a7 | 60 | struct omap_drm_irq vblank_irq; |
f5f9454c RC |
61 | struct omap_drm_irq error_irq; |
62 | ||
a42133a7 LP |
63 | /* list of framebuffers to unpin */ |
64 | struct list_head pending_unpins; | |
cd5351f4 | 65 | |
42fb61cc | 66 | /* |
15d02e92 LP |
67 | * flip_state flag indicates the current page flap state: IDLE if no |
68 | * page queue has been submitted, WAIT when waiting for GEM async | |
69 | * completion, QUEUED when the page flip has been queued to the hardware | |
70 | * or CANCELLED when the CRTC is turned off before the flip gets queued | |
0c19ac9d LP |
71 | * to the hardware. The flip event, if any, is stored in flip_event, and |
72 | * the framebuffer queued for page flip is stored in flip_fb. The | |
15d02e92 | 73 | * flip_wait wait queue is used to wait for page flip completion. |
42fb61cc LP |
74 | * |
75 | * The flip_work work queue handles page flip requests without caring | |
76 | * about what context the GEM async callback is called from. Possibly we | |
77 | * should just make omap_gem always call the cb from the worker so we | |
78 | * don't have to care about this. | |
79 | */ | |
15d02e92 | 80 | enum omap_page_flip_state flip_state; |
42fb61cc | 81 | struct drm_pending_vblank_event *flip_event; |
0c19ac9d | 82 | struct drm_framebuffer *flip_fb; |
c397cfd4 | 83 | wait_queue_head_t flip_wait; |
42fb61cc | 84 | struct work_struct flip_work; |
f5f9454c | 85 | |
a42133a7 LP |
86 | struct completion completion; |
87 | ||
a36af73f | 88 | bool ignore_digit_sync_lost; |
f5f9454c RC |
89 | }; |
90 | ||
a42133a7 LP |
91 | struct omap_framebuffer_unpin { |
92 | struct list_head list; | |
93 | struct drm_framebuffer *fb; | |
94 | }; | |
95 | ||
971fb3e5 LP |
96 | /* ----------------------------------------------------------------------------- |
97 | * Helper Functions | |
98 | */ | |
99 | ||
0d8f371f AT |
100 | uint32_t pipe2vbl(struct drm_crtc *crtc) |
101 | { | |
102 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
103 | ||
104 | return dispc_mgr_get_vsync_irq(omap_crtc->channel); | |
105 | } | |
106 | ||
971fb3e5 LP |
107 | const struct omap_video_timings *omap_crtc_timings(struct drm_crtc *crtc) |
108 | { | |
109 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
110 | return &omap_crtc->timings; | |
111 | } | |
112 | ||
113 | enum omap_channel omap_crtc_channel(struct drm_crtc *crtc) | |
114 | { | |
115 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
116 | return omap_crtc->channel; | |
117 | } | |
118 | ||
119 | /* ----------------------------------------------------------------------------- | |
120 | * DSS Manager Functions | |
121 | */ | |
122 | ||
f5f9454c RC |
123 | /* |
124 | * Manager-ops, callbacks from output when they need to configure | |
125 | * the upstream part of the video pipe. | |
126 | * | |
127 | * Most of these we can ignore until we add support for command-mode | |
128 | * panels.. for video-mode the crtc-helpers already do an adequate | |
129 | * job of sequencing the setup of the video pipe in the proper order | |
130 | */ | |
131 | ||
04b1fc02 TV |
132 | /* ovl-mgr-id -> crtc */ |
133 | static struct omap_crtc *omap_crtcs[8]; | |
134 | ||
f5f9454c | 135 | /* we can probably ignore these until we support command-mode panels: */ |
4343f0f8 | 136 | static int omap_crtc_dss_connect(struct omap_overlay_manager *mgr, |
1f68d9c4 | 137 | struct omap_dss_device *dst) |
a7e71e7f TV |
138 | { |
139 | if (mgr->output) | |
140 | return -EINVAL; | |
141 | ||
142 | if ((mgr->supported_outputs & dst->id) == 0) | |
143 | return -EINVAL; | |
144 | ||
145 | dst->manager = mgr; | |
146 | mgr->output = dst; | |
147 | ||
148 | return 0; | |
149 | } | |
150 | ||
4343f0f8 | 151 | static void omap_crtc_dss_disconnect(struct omap_overlay_manager *mgr, |
1f68d9c4 | 152 | struct omap_dss_device *dst) |
a7e71e7f TV |
153 | { |
154 | mgr->output->manager = NULL; | |
155 | mgr->output = NULL; | |
156 | } | |
157 | ||
4343f0f8 | 158 | static void omap_crtc_dss_start_update(struct omap_overlay_manager *mgr) |
f5f9454c RC |
159 | { |
160 | } | |
161 | ||
a42133a7 | 162 | /* Called only from omap_crtc_setup and suspend/resume handlers. */ |
8472b570 LP |
163 | static void omap_crtc_set_enabled(struct drm_crtc *crtc, bool enable) |
164 | { | |
165 | struct drm_device *dev = crtc->dev; | |
166 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
167 | enum omap_channel channel = omap_crtc->channel; | |
168 | struct omap_irq_wait *wait; | |
169 | u32 framedone_irq, vsync_irq; | |
170 | int ret; | |
171 | ||
172 | if (dispc_mgr_is_enabled(channel) == enable) | |
173 | return; | |
174 | ||
ef422283 TV |
175 | if (omap_crtc->channel == OMAP_DSS_CHANNEL_DIGIT) { |
176 | /* | |
177 | * Digit output produces some sync lost interrupts during the | |
178 | * first frame when enabling, so we need to ignore those. | |
179 | */ | |
180 | omap_crtc->ignore_digit_sync_lost = true; | |
181 | } | |
8472b570 LP |
182 | |
183 | framedone_irq = dispc_mgr_get_framedone_irq(channel); | |
184 | vsync_irq = dispc_mgr_get_vsync_irq(channel); | |
185 | ||
186 | if (enable) { | |
187 | wait = omap_irq_wait_init(dev, vsync_irq, 1); | |
188 | } else { | |
189 | /* | |
190 | * When we disable the digit output, we need to wait for | |
191 | * FRAMEDONE to know that DISPC has finished with the output. | |
192 | * | |
193 | * OMAP2/3 does not have FRAMEDONE irq for digit output, and in | |
194 | * that case we need to use vsync interrupt, and wait for both | |
195 | * even and odd frames. | |
196 | */ | |
197 | ||
198 | if (framedone_irq) | |
199 | wait = omap_irq_wait_init(dev, framedone_irq, 1); | |
200 | else | |
201 | wait = omap_irq_wait_init(dev, vsync_irq, 2); | |
202 | } | |
203 | ||
204 | dispc_mgr_enable(channel, enable); | |
205 | ||
206 | ret = omap_irq_wait(dev, wait, msecs_to_jiffies(100)); | |
207 | if (ret) { | |
208 | dev_err(dev->dev, "%s: timeout waiting for %s\n", | |
209 | omap_crtc->name, enable ? "enable" : "disable"); | |
210 | } | |
211 | ||
ef422283 TV |
212 | if (omap_crtc->channel == OMAP_DSS_CHANNEL_DIGIT) { |
213 | omap_crtc->ignore_digit_sync_lost = false; | |
214 | /* make sure the irq handler sees the value above */ | |
215 | mb(); | |
216 | } | |
8472b570 LP |
217 | } |
218 | ||
506096a1 | 219 | |
4343f0f8 | 220 | static int omap_crtc_dss_enable(struct omap_overlay_manager *mgr) |
f5f9454c | 221 | { |
506096a1 TV |
222 | struct omap_crtc *omap_crtc = omap_crtcs[mgr->id]; |
223 | ||
224 | dispc_mgr_setup(omap_crtc->channel, &omap_crtc->info); | |
225 | dispc_mgr_set_timings(omap_crtc->channel, | |
226 | &omap_crtc->timings); | |
8472b570 | 227 | omap_crtc_set_enabled(&omap_crtc->base, true); |
506096a1 | 228 | |
f5f9454c RC |
229 | return 0; |
230 | } | |
231 | ||
4343f0f8 | 232 | static void omap_crtc_dss_disable(struct omap_overlay_manager *mgr) |
f5f9454c | 233 | { |
506096a1 TV |
234 | struct omap_crtc *omap_crtc = omap_crtcs[mgr->id]; |
235 | ||
8472b570 | 236 | omap_crtc_set_enabled(&omap_crtc->base, false); |
f5f9454c RC |
237 | } |
238 | ||
4343f0f8 | 239 | static void omap_crtc_dss_set_timings(struct omap_overlay_manager *mgr, |
f5f9454c RC |
240 | const struct omap_video_timings *timings) |
241 | { | |
04b1fc02 | 242 | struct omap_crtc *omap_crtc = omap_crtcs[mgr->id]; |
f5f9454c RC |
243 | DBG("%s", omap_crtc->name); |
244 | omap_crtc->timings = *timings; | |
f5f9454c RC |
245 | } |
246 | ||
4343f0f8 | 247 | static void omap_crtc_dss_set_lcd_config(struct omap_overlay_manager *mgr, |
f5f9454c RC |
248 | const struct dss_lcd_mgr_config *config) |
249 | { | |
04b1fc02 | 250 | struct omap_crtc *omap_crtc = omap_crtcs[mgr->id]; |
f5f9454c RC |
251 | DBG("%s", omap_crtc->name); |
252 | dispc_mgr_set_lcd_config(omap_crtc->channel, config); | |
253 | } | |
254 | ||
4343f0f8 | 255 | static int omap_crtc_dss_register_framedone( |
f5f9454c RC |
256 | struct omap_overlay_manager *mgr, |
257 | void (*handler)(void *), void *data) | |
258 | { | |
259 | return 0; | |
260 | } | |
261 | ||
4343f0f8 | 262 | static void omap_crtc_dss_unregister_framedone( |
f5f9454c RC |
263 | struct omap_overlay_manager *mgr, |
264 | void (*handler)(void *), void *data) | |
265 | { | |
266 | } | |
267 | ||
268 | static const struct dss_mgr_ops mgr_ops = { | |
4343f0f8 LP |
269 | .connect = omap_crtc_dss_connect, |
270 | .disconnect = omap_crtc_dss_disconnect, | |
271 | .start_update = omap_crtc_dss_start_update, | |
272 | .enable = omap_crtc_dss_enable, | |
273 | .disable = omap_crtc_dss_disable, | |
274 | .set_timings = omap_crtc_dss_set_timings, | |
275 | .set_lcd_config = omap_crtc_dss_set_lcd_config, | |
276 | .register_framedone_handler = omap_crtc_dss_register_framedone, | |
277 | .unregister_framedone_handler = omap_crtc_dss_unregister_framedone, | |
cd5351f4 RC |
278 | }; |
279 | ||
971fb3e5 | 280 | /* ----------------------------------------------------------------------------- |
1d5e5ea1 | 281 | * Setup, Flush and Page Flip |
971fb3e5 LP |
282 | */ |
283 | ||
1d5e5ea1 LP |
284 | void omap_crtc_cancel_page_flip(struct drm_crtc *crtc, struct drm_file *file) |
285 | { | |
286 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
287 | struct drm_device *dev = crtc->dev; | |
288 | unsigned long flags; | |
289 | ||
290 | spin_lock_irqsave(&dev->event_lock, flags); | |
291 | ||
292 | /* Only complete events queued for our file handle. */ | |
293 | if (omap_crtc->flip_event && | |
294 | file == omap_crtc->flip_event->base.file_priv) { | |
295 | drm_send_vblank_event(dev, omap_crtc->pipe, | |
296 | omap_crtc->flip_event); | |
297 | omap_crtc->flip_event = NULL; | |
298 | } | |
299 | ||
300 | spin_unlock_irqrestore(&dev->event_lock, flags); | |
301 | } | |
302 | ||
15d02e92 LP |
303 | /* Must be called with dev->event_lock locked. */ |
304 | static void omap_crtc_complete_page_flip(struct drm_crtc *crtc, | |
305 | enum omap_page_flip_state state) | |
306 | { | |
307 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
308 | struct drm_device *dev = crtc->dev; | |
309 | ||
310 | if (omap_crtc->flip_event) { | |
311 | drm_send_vblank_event(dev, omap_crtc->pipe, | |
312 | omap_crtc->flip_event); | |
313 | omap_crtc->flip_event = NULL; | |
314 | } | |
315 | ||
316 | omap_crtc->flip_state = state; | |
c397cfd4 LP |
317 | |
318 | if (state == OMAP_PAGE_FLIP_IDLE) | |
319 | wake_up(&omap_crtc->flip_wait); | |
320 | } | |
321 | ||
322 | static bool omap_crtc_page_flip_pending(struct drm_crtc *crtc) | |
323 | { | |
324 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
325 | struct drm_device *dev = crtc->dev; | |
326 | unsigned long flags; | |
327 | bool pending; | |
328 | ||
329 | spin_lock_irqsave(&dev->event_lock, flags); | |
330 | pending = omap_crtc->flip_state != OMAP_PAGE_FLIP_IDLE; | |
331 | spin_unlock_irqrestore(&dev->event_lock, flags); | |
332 | ||
333 | return pending; | |
334 | } | |
335 | ||
336 | static void omap_crtc_wait_page_flip(struct drm_crtc *crtc) | |
337 | { | |
338 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
339 | struct drm_device *dev = crtc->dev; | |
340 | bool cancelled = false; | |
341 | unsigned long flags; | |
342 | ||
343 | /* | |
344 | * If we're still waiting for the GEM async operation to complete just | |
345 | * cancel the page flip, as we're holding the CRTC mutex preventing the | |
346 | * page flip work handler from queueing the page flip. | |
347 | * | |
348 | * We can't release the reference to the frame buffer here as the async | |
349 | * operation doesn't keep its own reference to the buffer. We'll just | |
350 | * let the page flip work queue handle that. | |
351 | */ | |
352 | spin_lock_irqsave(&dev->event_lock, flags); | |
353 | if (omap_crtc->flip_state == OMAP_PAGE_FLIP_WAIT) { | |
354 | omap_crtc_complete_page_flip(crtc, OMAP_PAGE_FLIP_CANCELLED); | |
355 | cancelled = true; | |
356 | } | |
357 | spin_unlock_irqrestore(&dev->event_lock, flags); | |
358 | ||
359 | if (cancelled) | |
360 | return; | |
361 | ||
362 | if (wait_event_timeout(omap_crtc->flip_wait, | |
363 | !omap_crtc_page_flip_pending(crtc), | |
364 | msecs_to_jiffies(50))) | |
365 | return; | |
366 | ||
367 | dev_warn(crtc->dev->dev, "page flip timeout!\n"); | |
368 | ||
369 | spin_lock_irqsave(&dev->event_lock, flags); | |
370 | omap_crtc_complete_page_flip(crtc, OMAP_PAGE_FLIP_IDLE); | |
371 | spin_unlock_irqrestore(&dev->event_lock, flags); | |
15d02e92 LP |
372 | } |
373 | ||
971fb3e5 LP |
374 | static void omap_crtc_error_irq(struct omap_drm_irq *irq, uint32_t irqstatus) |
375 | { | |
376 | struct omap_crtc *omap_crtc = | |
377 | container_of(irq, struct omap_crtc, error_irq); | |
a36af73f TV |
378 | |
379 | if (omap_crtc->ignore_digit_sync_lost) { | |
380 | irqstatus &= ~DISPC_IRQ_SYNC_LOST_DIGIT; | |
381 | if (!irqstatus) | |
382 | return; | |
383 | } | |
384 | ||
3b143fc8 | 385 | DRM_ERROR_RATELIMITED("%s: errors: %08x\n", omap_crtc->name, irqstatus); |
971fb3e5 LP |
386 | } |
387 | ||
a42133a7 | 388 | static void omap_crtc_vblank_irq(struct omap_drm_irq *irq, uint32_t irqstatus) |
971fb3e5 LP |
389 | { |
390 | struct omap_crtc *omap_crtc = | |
a42133a7 LP |
391 | container_of(irq, struct omap_crtc, vblank_irq); |
392 | struct drm_device *dev = omap_crtc->base.dev; | |
393 | unsigned long flags; | |
971fb3e5 | 394 | |
a42133a7 LP |
395 | if (dispc_mgr_go_busy(omap_crtc->channel)) |
396 | return; | |
397 | ||
398 | DBG("%s: apply done", omap_crtc->name); | |
399 | __omap_irq_unregister(dev, &omap_crtc->vblank_irq); | |
400 | ||
a42133a7 | 401 | /* wakeup userspace */ |
15d02e92 LP |
402 | spin_lock_irqsave(&dev->event_lock, flags); |
403 | omap_crtc_complete_page_flip(&omap_crtc->base, OMAP_PAGE_FLIP_IDLE); | |
a42133a7 LP |
404 | spin_unlock_irqrestore(&dev->event_lock, flags); |
405 | ||
406 | complete(&omap_crtc->completion); | |
971fb3e5 LP |
407 | } |
408 | ||
a42133a7 | 409 | int omap_crtc_flush(struct drm_crtc *crtc) |
971fb3e5 | 410 | { |
a42133a7 LP |
411 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); |
412 | struct omap_framebuffer_unpin *fb, *next; | |
971fb3e5 | 413 | |
a42133a7 | 414 | DBG("%s: GO", omap_crtc->name); |
971fb3e5 | 415 | |
a42133a7 LP |
416 | WARN_ON(!drm_modeset_is_locked(&crtc->mutex)); |
417 | WARN_ON(omap_crtc->vblank_irq.registered); | |
971fb3e5 | 418 | |
a42133a7 | 419 | dispc_runtime_get(); |
971fb3e5 | 420 | |
a42133a7 LP |
421 | if (dispc_mgr_is_enabled(omap_crtc->channel)) { |
422 | dispc_mgr_go(omap_crtc->channel); | |
423 | omap_irq_register(crtc->dev, &omap_crtc->vblank_irq); | |
971fb3e5 | 424 | |
a42133a7 LP |
425 | WARN_ON(!wait_for_completion_timeout(&omap_crtc->completion, |
426 | msecs_to_jiffies(100))); | |
427 | reinit_completion(&omap_crtc->completion); | |
428 | } | |
971fb3e5 | 429 | |
a42133a7 | 430 | dispc_runtime_put(); |
971fb3e5 | 431 | |
a42133a7 LP |
432 | /* Unpin and unreference pending framebuffers. */ |
433 | list_for_each_entry_safe(fb, next, &omap_crtc->pending_unpins, list) { | |
434 | omap_framebuffer_unpin(fb->fb); | |
435 | drm_framebuffer_unreference(fb->fb); | |
436 | list_del(&fb->list); | |
437 | kfree(fb); | |
971fb3e5 LP |
438 | } |
439 | ||
a42133a7 | 440 | return 0; |
971fb3e5 LP |
441 | } |
442 | ||
a42133a7 | 443 | int omap_crtc_queue_unpin(struct drm_crtc *crtc, struct drm_framebuffer *fb) |
971fb3e5 LP |
444 | { |
445 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
a42133a7 | 446 | struct omap_framebuffer_unpin *unpin; |
971fb3e5 | 447 | |
a42133a7 LP |
448 | unpin = kzalloc(sizeof(*unpin), GFP_KERNEL); |
449 | if (!unpin) | |
450 | return -ENOMEM; | |
971fb3e5 | 451 | |
a42133a7 LP |
452 | unpin->fb = fb; |
453 | list_add_tail(&unpin->list, &omap_crtc->pending_unpins); | |
971fb3e5 LP |
454 | |
455 | return 0; | |
456 | } | |
457 | ||
a42133a7 | 458 | static void omap_crtc_setup(struct drm_crtc *crtc) |
971fb3e5 | 459 | { |
a42133a7 | 460 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); |
971fb3e5 LP |
461 | struct omap_drm_private *priv = crtc->dev->dev_private; |
462 | struct drm_encoder *encoder = NULL; | |
463 | unsigned int i; | |
464 | ||
465 | DBG("%s: enabled=%d", omap_crtc->name, omap_crtc->enabled); | |
466 | ||
a42133a7 LP |
467 | dispc_runtime_get(); |
468 | ||
971fb3e5 LP |
469 | for (i = 0; i < priv->num_encoders; i++) { |
470 | if (priv->encoders[i]->crtc == crtc) { | |
471 | encoder = priv->encoders[i]; | |
472 | break; | |
473 | } | |
474 | } | |
475 | ||
476 | if (omap_crtc->current_encoder && encoder != omap_crtc->current_encoder) | |
477 | omap_encoder_set_enabled(omap_crtc->current_encoder, false); | |
478 | ||
479 | omap_crtc->current_encoder = encoder; | |
480 | ||
481 | if (!omap_crtc->enabled) { | |
482 | if (encoder) | |
483 | omap_encoder_set_enabled(encoder, false); | |
484 | } else { | |
485 | if (encoder) { | |
486 | omap_encoder_set_enabled(encoder, false); | |
487 | omap_encoder_update(encoder, omap_crtc->mgr, | |
488 | &omap_crtc->timings); | |
489 | omap_encoder_set_enabled(encoder, true); | |
490 | } | |
491 | } | |
971fb3e5 | 492 | |
a42133a7 | 493 | dispc_runtime_put(); |
971fb3e5 LP |
494 | } |
495 | ||
496 | /* ----------------------------------------------------------------------------- | |
497 | * CRTC Functions | |
f5f9454c RC |
498 | */ |
499 | ||
cd5351f4 RC |
500 | static void omap_crtc_destroy(struct drm_crtc *crtc) |
501 | { | |
502 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
f5f9454c RC |
503 | |
504 | DBG("%s", omap_crtc->name); | |
505 | ||
a42133a7 | 506 | WARN_ON(omap_crtc->vblank_irq.registered); |
f5f9454c RC |
507 | omap_irq_unregister(crtc->dev, &omap_crtc->error_irq); |
508 | ||
cd5351f4 | 509 | drm_crtc_cleanup(crtc); |
f5f9454c | 510 | |
cd5351f4 RC |
511 | kfree(omap_crtc); |
512 | } | |
513 | ||
f1d57fb5 LP |
514 | static bool omap_crtc_mode_fixup(struct drm_crtc *crtc, |
515 | const struct drm_display_mode *mode, | |
516 | struct drm_display_mode *adjusted_mode) | |
517 | { | |
518 | return true; | |
519 | } | |
520 | ||
521 | static void omap_crtc_enable(struct drm_crtc *crtc) | |
cd5351f4 | 522 | { |
bb5c2d9a | 523 | struct omap_drm_private *priv = crtc->dev->dev_private; |
cd5351f4 | 524 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); |
f1d57fb5 | 525 | unsigned int i; |
cd5351f4 | 526 | |
f1d57fb5 | 527 | DBG("%s", omap_crtc->name); |
f5f9454c | 528 | |
f1d57fb5 | 529 | if (omap_crtc->enabled) |
a42133a7 | 530 | return; |
cd5351f4 | 531 | |
f1d57fb5 | 532 | /* Enable all planes associated with the CRTC. */ |
a42133a7 LP |
533 | for (i = 0; i < priv->num_planes; i++) { |
534 | struct drm_plane *plane = priv->planes[i]; | |
535 | ||
536 | if (plane->crtc == crtc) | |
f1d57fb5 | 537 | WARN_ON(omap_plane_set_enable(plane, true)); |
cd5351f4 | 538 | } |
a42133a7 | 539 | |
f1d57fb5 | 540 | omap_crtc->enabled = true; |
a42133a7 LP |
541 | |
542 | omap_crtc_setup(crtc); | |
543 | omap_crtc_flush(crtc); | |
c397cfd4 | 544 | |
f1d57fb5 LP |
545 | dispc_runtime_get(); |
546 | drm_crtc_vblank_on(crtc); | |
547 | dispc_runtime_put(); | |
cd5351f4 RC |
548 | } |
549 | ||
f1d57fb5 | 550 | static void omap_crtc_disable(struct drm_crtc *crtc) |
cd5351f4 | 551 | { |
f1d57fb5 LP |
552 | struct omap_drm_private *priv = crtc->dev->dev_private; |
553 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
554 | unsigned int i; | |
555 | ||
556 | DBG("%s", omap_crtc->name); | |
557 | ||
558 | if (!omap_crtc->enabled) | |
559 | return; | |
560 | ||
561 | omap_crtc_wait_page_flip(crtc); | |
562 | dispc_runtime_get(); | |
563 | drm_crtc_vblank_off(crtc); | |
564 | dispc_runtime_put(); | |
565 | ||
566 | /* Disable all planes associated with the CRTC. */ | |
567 | for (i = 0; i < priv->num_planes; i++) { | |
568 | struct drm_plane *plane = priv->planes[i]; | |
569 | ||
570 | if (plane->crtc == crtc) | |
571 | WARN_ON(omap_plane_set_enable(plane, false)); | |
572 | } | |
573 | ||
f7a73b65 LP |
574 | /* |
575 | * HACK: Unpin the primary plane frame buffer if we're disabled without | |
576 | * going through full mode set. | |
577 | * HACK: The legacy set config helper drm_crtc_helper_set_config() that | |
578 | * we still use calls the .disable() operation directly when called with | |
579 | * a NULL frame buffer (for instance from drm_fb_release()). As a result | |
580 | * the CRTC is disabled without going through a full mode set, and the | |
581 | * primary plane' framebuffer is kept pin. Unpin it manually here until | |
582 | * we switch to the atomic set config helper. | |
583 | */ | |
584 | if (crtc->primary->fb) { | |
585 | const struct drm_plane_helper_funcs *funcs; | |
586 | ||
587 | funcs = crtc->primary->helper_private; | |
588 | funcs->cleanup_fb(crtc->primary, crtc->primary->fb, NULL); | |
589 | } | |
590 | ||
f1d57fb5 LP |
591 | omap_crtc->enabled = false; |
592 | ||
593 | omap_crtc_setup(crtc); | |
594 | omap_crtc_flush(crtc); | |
cd5351f4 RC |
595 | } |
596 | ||
f7a73b65 | 597 | static void omap_crtc_mode_set_nofb(struct drm_crtc *crtc) |
cd5351f4 RC |
598 | { |
599 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
f7a73b65 | 600 | struct drm_display_mode *mode = &crtc->state->adjusted_mode; |
f5f9454c RC |
601 | |
602 | DBG("%s: set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x", | |
f7a73b65 LP |
603 | omap_crtc->name, mode->base.id, mode->name, |
604 | mode->vrefresh, mode->clock, | |
605 | mode->hdisplay, mode->hsync_start, mode->hsync_end, mode->htotal, | |
606 | mode->vdisplay, mode->vsync_start, mode->vsync_end, mode->vtotal, | |
607 | mode->type, mode->flags); | |
f5f9454c RC |
608 | |
609 | copy_timings_drm_to_omap(&omap_crtc->timings, mode); | |
cd5351f4 RC |
610 | } |
611 | ||
f1d57fb5 LP |
612 | static void omap_crtc_dpms(struct drm_crtc *crtc, int mode) |
613 | { | |
614 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
615 | bool enable = (mode == DRM_MODE_DPMS_ON); | |
616 | ||
617 | DBG("%s: %d", omap_crtc->name, mode); | |
618 | ||
619 | if (enable) | |
620 | omap_crtc_enable(crtc); | |
621 | else | |
622 | omap_crtc_disable(crtc); | |
623 | } | |
624 | ||
cd5351f4 RC |
625 | static void omap_crtc_prepare(struct drm_crtc *crtc) |
626 | { | |
627 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
bb5c2d9a | 628 | DBG("%s", omap_crtc->name); |
cd5351f4 RC |
629 | omap_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); |
630 | } | |
631 | ||
632 | static void omap_crtc_commit(struct drm_crtc *crtc) | |
633 | { | |
634 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
bb5c2d9a | 635 | DBG("%s", omap_crtc->name); |
cd5351f4 RC |
636 | omap_crtc_dpms(crtc, DRM_MODE_DPMS_ON); |
637 | } | |
638 | ||
de8e4100 LP |
639 | static void omap_crtc_atomic_begin(struct drm_crtc *crtc) |
640 | { | |
641 | dispc_runtime_get(); | |
642 | } | |
643 | ||
644 | static void omap_crtc_atomic_flush(struct drm_crtc *crtc) | |
645 | { | |
646 | omap_crtc_flush(crtc); | |
647 | ||
648 | dispc_runtime_put(); | |
649 | } | |
650 | ||
f5f9454c | 651 | static void page_flip_worker(struct work_struct *work) |
72d0c336 | 652 | { |
f5f9454c | 653 | struct omap_crtc *omap_crtc = |
42fb61cc | 654 | container_of(work, struct omap_crtc, flip_work); |
f5f9454c | 655 | struct drm_crtc *crtc = &omap_crtc->base; |
f5f9454c | 656 | struct drm_display_mode *mode = &crtc->mode; |
15d02e92 LP |
657 | struct drm_device *dev = crtc->dev; |
658 | struct drm_framebuffer *fb; | |
119c0814 | 659 | struct drm_gem_object *bo; |
15d02e92 LP |
660 | unsigned long flags; |
661 | bool queue_flip; | |
72d0c336 | 662 | |
51fd371b | 663 | drm_modeset_lock(&crtc->mutex, NULL); |
15d02e92 LP |
664 | |
665 | spin_lock_irqsave(&dev->event_lock, flags); | |
0c19ac9d | 666 | |
15d02e92 LP |
667 | /* |
668 | * The page flip could have been cancelled while waiting for the GEM | |
669 | * async operation to complete. Don't queue the flip in that case. | |
670 | */ | |
671 | if (omap_crtc->flip_state == OMAP_PAGE_FLIP_WAIT) { | |
672 | omap_crtc->flip_state = OMAP_PAGE_FLIP_QUEUED; | |
673 | queue_flip = true; | |
674 | } else { | |
675 | omap_crtc->flip_state = OMAP_PAGE_FLIP_IDLE; | |
676 | queue_flip = false; | |
677 | } | |
15d02e92 | 678 | |
0c19ac9d LP |
679 | fb = omap_crtc->flip_fb; |
680 | omap_crtc->flip_fb = NULL; | |
681 | ||
682 | spin_unlock_irqrestore(&dev->event_lock, flags); | |
15d02e92 LP |
683 | |
684 | if (queue_flip) { | |
685 | omap_plane_mode_set(crtc->primary, crtc, fb, | |
686 | 0, 0, mode->hdisplay, mode->vdisplay, | |
687 | crtc->x, crtc->y, mode->hdisplay, | |
688 | mode->vdisplay); | |
689 | omap_crtc_flush(crtc); | |
690 | } | |
691 | ||
51fd371b | 692 | drm_modeset_unlock(&crtc->mutex); |
119c0814 | 693 | |
15d02e92 | 694 | bo = omap_framebuffer_bo(fb, 0); |
119c0814 | 695 | drm_gem_object_unreference_unlocked(bo); |
0c19ac9d | 696 | drm_framebuffer_unreference(fb); |
72d0c336 RC |
697 | } |
698 | ||
f5f9454c RC |
699 | static void page_flip_cb(void *arg) |
700 | { | |
701 | struct drm_crtc *crtc = arg; | |
702 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
703 | struct omap_drm_private *priv = crtc->dev->dev_private; | |
704 | ||
705 | /* avoid assumptions about what ctxt we are called from: */ | |
42fb61cc | 706 | queue_work(priv->wq, &omap_crtc->flip_work); |
f5f9454c RC |
707 | } |
708 | ||
077db4da LP |
709 | static int omap_crtc_page_flip(struct drm_crtc *crtc, |
710 | struct drm_framebuffer *fb, | |
711 | struct drm_pending_vblank_event *event, | |
712 | uint32_t page_flip_flags) | |
cd5351f4 RC |
713 | { |
714 | struct drm_device *dev = crtc->dev; | |
715 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
f4510a27 | 716 | struct drm_plane *primary = crtc->primary; |
119c0814 | 717 | struct drm_gem_object *bo; |
38e5597a | 718 | unsigned long flags; |
cd5351f4 | 719 | |
f4510a27 | 720 | DBG("%d -> %d (event=%p)", primary->fb ? primary->fb->base.id : -1, |
f5f9454c | 721 | fb->base.id, event); |
cd5351f4 | 722 | |
38e5597a AT |
723 | spin_lock_irqsave(&dev->event_lock, flags); |
724 | ||
15d02e92 | 725 | if (omap_crtc->flip_state != OMAP_PAGE_FLIP_IDLE) { |
38e5597a | 726 | spin_unlock_irqrestore(&dev->event_lock, flags); |
cd5351f4 | 727 | dev_err(dev->dev, "already a pending flip\n"); |
549a7549 | 728 | return -EBUSY; |
cd5351f4 RC |
729 | } |
730 | ||
0c19ac9d LP |
731 | /* |
732 | * Store a reference to the framebuffer queued for page flip in the CRTC | |
733 | * private structure. We can't rely on crtc->primary->fb in the page | |
734 | * flip worker, as a racing CRTC disable (due for instance to an | |
735 | * explicit framebuffer deletion from userspace) would set that field to | |
736 | * NULL before the worker gets a change to run. | |
737 | */ | |
738 | drm_framebuffer_reference(fb); | |
739 | omap_crtc->flip_fb = fb; | |
42fb61cc | 740 | omap_crtc->flip_event = event; |
15d02e92 | 741 | omap_crtc->flip_state = OMAP_PAGE_FLIP_WAIT; |
0c19ac9d | 742 | |
69a12263 | 743 | drm_atomic_set_fb_for_plane(primary->state, fb); |
42fb61cc | 744 | primary->fb = fb; |
cd5351f4 | 745 | |
38e5597a AT |
746 | spin_unlock_irqrestore(&dev->event_lock, flags); |
747 | ||
119c0814 RC |
748 | /* |
749 | * Hold a reference temporarily until the crtc is updated | |
750 | * and takes the reference to the bo. This avoids it | |
751 | * getting freed from under us: | |
752 | */ | |
753 | bo = omap_framebuffer_bo(fb, 0); | |
754 | drm_gem_object_reference(bo); | |
755 | ||
756 | omap_gem_op_async(bo, OMAP_GEM_READ, page_flip_cb, crtc); | |
cd5351f4 RC |
757 | |
758 | return 0; | |
759 | } | |
760 | ||
3c810c61 RC |
761 | static int omap_crtc_set_property(struct drm_crtc *crtc, |
762 | struct drm_property *property, uint64_t val) | |
763 | { | |
e2cd09b2 | 764 | if (property == crtc->dev->mode_config.rotation_property) { |
1e0fdfc2 RC |
765 | crtc->invert_dimensions = |
766 | !!(val & ((1LL << DRM_ROTATE_90) | (1LL << DRM_ROTATE_270))); | |
767 | } | |
768 | ||
ef6b0e02 | 769 | return omap_plane_set_property(crtc->primary, property, val); |
3c810c61 RC |
770 | } |
771 | ||
cd5351f4 | 772 | static const struct drm_crtc_funcs omap_crtc_funcs = { |
69a12263 | 773 | .reset = drm_atomic_helper_crtc_reset, |
cd5351f4 RC |
774 | .set_config = drm_crtc_helper_set_config, |
775 | .destroy = omap_crtc_destroy, | |
077db4da | 776 | .page_flip = omap_crtc_page_flip, |
3c810c61 | 777 | .set_property = omap_crtc_set_property, |
69a12263 LP |
778 | .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, |
779 | .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, | |
cd5351f4 RC |
780 | }; |
781 | ||
782 | static const struct drm_crtc_helper_funcs omap_crtc_helper_funcs = { | |
783 | .dpms = omap_crtc_dpms, | |
784 | .mode_fixup = omap_crtc_mode_fixup, | |
f7a73b65 | 785 | .mode_set = drm_helper_crtc_mode_set, |
cd5351f4 RC |
786 | .prepare = omap_crtc_prepare, |
787 | .commit = omap_crtc_commit, | |
f7a73b65 LP |
788 | .mode_set_nofb = omap_crtc_mode_set_nofb, |
789 | .mode_set_base = drm_helper_crtc_mode_set_base, | |
f1d57fb5 LP |
790 | .disable = omap_crtc_disable, |
791 | .enable = omap_crtc_enable, | |
de8e4100 LP |
792 | .atomic_begin = omap_crtc_atomic_begin, |
793 | .atomic_flush = omap_crtc_atomic_flush, | |
cd5351f4 RC |
794 | }; |
795 | ||
971fb3e5 LP |
796 | /* ----------------------------------------------------------------------------- |
797 | * Init and Cleanup | |
798 | */ | |
e2f8fd74 | 799 | |
f5f9454c | 800 | static const char *channel_names[] = { |
222025e4 LP |
801 | [OMAP_DSS_CHANNEL_LCD] = "lcd", |
802 | [OMAP_DSS_CHANNEL_DIGIT] = "tv", | |
803 | [OMAP_DSS_CHANNEL_LCD2] = "lcd2", | |
804 | [OMAP_DSS_CHANNEL_LCD3] = "lcd3", | |
f5f9454c RC |
805 | }; |
806 | ||
04b1fc02 TV |
807 | void omap_crtc_pre_init(void) |
808 | { | |
809 | dss_install_mgr_ops(&mgr_ops); | |
810 | } | |
811 | ||
3a01ab25 AT |
812 | void omap_crtc_pre_uninit(void) |
813 | { | |
814 | dss_uninstall_mgr_ops(); | |
815 | } | |
816 | ||
cd5351f4 RC |
817 | /* initialize crtc */ |
818 | struct drm_crtc *omap_crtc_init(struct drm_device *dev, | |
f5f9454c | 819 | struct drm_plane *plane, enum omap_channel channel, int id) |
cd5351f4 RC |
820 | { |
821 | struct drm_crtc *crtc = NULL; | |
f5f9454c RC |
822 | struct omap_crtc *omap_crtc; |
823 | struct omap_overlay_manager_info *info; | |
ef6b0e02 | 824 | int ret; |
f5f9454c RC |
825 | |
826 | DBG("%s", channel_names[channel]); | |
cd5351f4 | 827 | |
f5f9454c | 828 | omap_crtc = kzalloc(sizeof(*omap_crtc), GFP_KERNEL); |
78110bb8 | 829 | if (!omap_crtc) |
ef6b0e02 | 830 | return NULL; |
cd5351f4 | 831 | |
cd5351f4 | 832 | crtc = &omap_crtc->base; |
bb5c2d9a | 833 | |
42fb61cc | 834 | INIT_WORK(&omap_crtc->flip_work, page_flip_worker); |
c397cfd4 | 835 | init_waitqueue_head(&omap_crtc->flip_wait); |
f5f9454c | 836 | |
a42133a7 | 837 | INIT_LIST_HEAD(&omap_crtc->pending_unpins); |
f5f9454c | 838 | |
a42133a7 | 839 | init_completion(&omap_crtc->completion); |
f5f9454c | 840 | |
0d8f371f | 841 | omap_crtc->channel = channel; |
0d8f371f AT |
842 | omap_crtc->name = channel_names[channel]; |
843 | omap_crtc->pipe = id; | |
844 | ||
a42133a7 LP |
845 | omap_crtc->vblank_irq.irqmask = pipe2vbl(crtc); |
846 | omap_crtc->vblank_irq.irq = omap_crtc_vblank_irq; | |
f5f9454c RC |
847 | |
848 | omap_crtc->error_irq.irqmask = | |
849 | dispc_mgr_get_sync_lost_irq(channel); | |
850 | omap_crtc->error_irq.irq = omap_crtc_error_irq; | |
851 | omap_irq_register(dev, &omap_crtc->error_irq); | |
852 | ||
f5f9454c | 853 | /* temporary: */ |
04b1fc02 | 854 | omap_crtc->mgr = omap_dss_get_overlay_manager(channel); |
f5f9454c RC |
855 | |
856 | /* TODO: fix hard-coded setup.. add properties! */ | |
857 | info = &omap_crtc->info; | |
858 | info->default_color = 0x00000000; | |
859 | info->trans_key = 0x00000000; | |
860 | info->trans_key_type = OMAP_DSS_COLOR_KEY_GFX_DST; | |
861 | info->trans_enabled = false; | |
bb5c2d9a | 862 | |
ef6b0e02 LP |
863 | ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL, |
864 | &omap_crtc_funcs); | |
865 | if (ret < 0) { | |
866 | kfree(omap_crtc); | |
867 | return NULL; | |
868 | } | |
869 | ||
cd5351f4 RC |
870 | drm_crtc_helper_add(crtc, &omap_crtc_helper_funcs); |
871 | ||
ef6b0e02 | 872 | omap_plane_install_properties(crtc->primary, &crtc->base); |
3c810c61 | 873 | |
04b1fc02 TV |
874 | omap_crtcs[channel] = omap_crtc; |
875 | ||
cd5351f4 | 876 | return crtc; |
cd5351f4 | 877 | } |