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