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 | ||
33 | struct omap_crtc { | |
34 | struct drm_crtc base; | |
f5f9454c | 35 | |
bb5c2d9a | 36 | const char *name; |
f5f9454c | 37 | enum omap_channel channel; |
c7aef12f | 38 | struct drm_encoder *current_encoder; |
f5f9454c RC |
39 | |
40 | /* | |
41 | * Temporary: eventually this will go away, but it is needed | |
42 | * for now to keep the output's happy. (They only need | |
43 | * mgr->id.) Eventually this will be replaced w/ something | |
44 | * more common-panel-framework-y | |
45 | */ | |
04b1fc02 | 46 | struct omap_overlay_manager *mgr; |
f5f9454c RC |
47 | |
48 | struct omap_video_timings timings; | |
f5f9454c | 49 | |
a42133a7 | 50 | struct omap_drm_irq vblank_irq; |
f5f9454c RC |
51 | struct omap_drm_irq error_irq; |
52 | ||
fa16d262 LP |
53 | /* pending event */ |
54 | struct drm_pending_vblank_event *event; | |
c397cfd4 | 55 | wait_queue_head_t flip_wait; |
f5f9454c | 56 | |
a42133a7 LP |
57 | struct completion completion; |
58 | ||
a36af73f | 59 | bool ignore_digit_sync_lost; |
f5f9454c RC |
60 | }; |
61 | ||
971fb3e5 LP |
62 | /* ----------------------------------------------------------------------------- |
63 | * Helper Functions | |
64 | */ | |
65 | ||
0d8f371f AT |
66 | uint32_t pipe2vbl(struct drm_crtc *crtc) |
67 | { | |
68 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
69 | ||
70 | return dispc_mgr_get_vsync_irq(omap_crtc->channel); | |
71 | } | |
72 | ||
971fb3e5 LP |
73 | const struct omap_video_timings *omap_crtc_timings(struct drm_crtc *crtc) |
74 | { | |
75 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
76 | return &omap_crtc->timings; | |
77 | } | |
78 | ||
79 | enum omap_channel omap_crtc_channel(struct drm_crtc *crtc) | |
80 | { | |
81 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
82 | return omap_crtc->channel; | |
83 | } | |
84 | ||
85 | /* ----------------------------------------------------------------------------- | |
86 | * DSS Manager Functions | |
87 | */ | |
88 | ||
f5f9454c RC |
89 | /* |
90 | * Manager-ops, callbacks from output when they need to configure | |
91 | * the upstream part of the video pipe. | |
92 | * | |
93 | * Most of these we can ignore until we add support for command-mode | |
94 | * panels.. for video-mode the crtc-helpers already do an adequate | |
95 | * job of sequencing the setup of the video pipe in the proper order | |
96 | */ | |
97 | ||
04b1fc02 TV |
98 | /* ovl-mgr-id -> crtc */ |
99 | static struct omap_crtc *omap_crtcs[8]; | |
100 | ||
f5f9454c | 101 | /* we can probably ignore these until we support command-mode panels: */ |
4343f0f8 | 102 | static int omap_crtc_dss_connect(struct omap_overlay_manager *mgr, |
1f68d9c4 | 103 | struct omap_dss_device *dst) |
a7e71e7f TV |
104 | { |
105 | if (mgr->output) | |
106 | return -EINVAL; | |
107 | ||
108 | if ((mgr->supported_outputs & dst->id) == 0) | |
109 | return -EINVAL; | |
110 | ||
111 | dst->manager = mgr; | |
112 | mgr->output = dst; | |
113 | ||
114 | return 0; | |
115 | } | |
116 | ||
4343f0f8 | 117 | static void omap_crtc_dss_disconnect(struct omap_overlay_manager *mgr, |
1f68d9c4 | 118 | struct omap_dss_device *dst) |
a7e71e7f TV |
119 | { |
120 | mgr->output->manager = NULL; | |
121 | mgr->output = NULL; | |
122 | } | |
123 | ||
4343f0f8 | 124 | static void omap_crtc_dss_start_update(struct omap_overlay_manager *mgr) |
f5f9454c RC |
125 | { |
126 | } | |
127 | ||
6cca481c | 128 | /* Called only from omap_crtc_encoder_setup and suspend/resume handlers. */ |
8472b570 LP |
129 | static void omap_crtc_set_enabled(struct drm_crtc *crtc, bool enable) |
130 | { | |
131 | struct drm_device *dev = crtc->dev; | |
132 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
133 | enum omap_channel channel = omap_crtc->channel; | |
134 | struct omap_irq_wait *wait; | |
135 | u32 framedone_irq, vsync_irq; | |
136 | int ret; | |
137 | ||
138 | if (dispc_mgr_is_enabled(channel) == enable) | |
139 | return; | |
140 | ||
ef422283 TV |
141 | if (omap_crtc->channel == OMAP_DSS_CHANNEL_DIGIT) { |
142 | /* | |
143 | * Digit output produces some sync lost interrupts during the | |
144 | * first frame when enabling, so we need to ignore those. | |
145 | */ | |
146 | omap_crtc->ignore_digit_sync_lost = true; | |
147 | } | |
8472b570 LP |
148 | |
149 | framedone_irq = dispc_mgr_get_framedone_irq(channel); | |
150 | vsync_irq = dispc_mgr_get_vsync_irq(channel); | |
151 | ||
152 | if (enable) { | |
153 | wait = omap_irq_wait_init(dev, vsync_irq, 1); | |
154 | } else { | |
155 | /* | |
156 | * When we disable the digit output, we need to wait for | |
157 | * FRAMEDONE to know that DISPC has finished with the output. | |
158 | * | |
159 | * OMAP2/3 does not have FRAMEDONE irq for digit output, and in | |
160 | * that case we need to use vsync interrupt, and wait for both | |
161 | * even and odd frames. | |
162 | */ | |
163 | ||
164 | if (framedone_irq) | |
165 | wait = omap_irq_wait_init(dev, framedone_irq, 1); | |
166 | else | |
167 | wait = omap_irq_wait_init(dev, vsync_irq, 2); | |
168 | } | |
169 | ||
170 | dispc_mgr_enable(channel, enable); | |
171 | ||
172 | ret = omap_irq_wait(dev, wait, msecs_to_jiffies(100)); | |
173 | if (ret) { | |
174 | dev_err(dev->dev, "%s: timeout waiting for %s\n", | |
175 | omap_crtc->name, enable ? "enable" : "disable"); | |
176 | } | |
177 | ||
ef422283 TV |
178 | if (omap_crtc->channel == OMAP_DSS_CHANNEL_DIGIT) { |
179 | omap_crtc->ignore_digit_sync_lost = false; | |
180 | /* make sure the irq handler sees the value above */ | |
181 | mb(); | |
182 | } | |
8472b570 LP |
183 | } |
184 | ||
506096a1 | 185 | |
4343f0f8 | 186 | static int omap_crtc_dss_enable(struct omap_overlay_manager *mgr) |
f5f9454c | 187 | { |
506096a1 | 188 | struct omap_crtc *omap_crtc = omap_crtcs[mgr->id]; |
dee8260d | 189 | struct omap_overlay_manager_info info; |
506096a1 | 190 | |
dee8260d LP |
191 | memset(&info, 0, sizeof(info)); |
192 | info.default_color = 0x00000000; | |
193 | info.trans_key = 0x00000000; | |
194 | info.trans_key_type = OMAP_DSS_COLOR_KEY_GFX_DST; | |
195 | info.trans_enabled = false; | |
196 | ||
197 | dispc_mgr_setup(omap_crtc->channel, &info); | |
506096a1 TV |
198 | dispc_mgr_set_timings(omap_crtc->channel, |
199 | &omap_crtc->timings); | |
8472b570 | 200 | omap_crtc_set_enabled(&omap_crtc->base, true); |
506096a1 | 201 | |
f5f9454c RC |
202 | return 0; |
203 | } | |
204 | ||
4343f0f8 | 205 | static void omap_crtc_dss_disable(struct omap_overlay_manager *mgr) |
f5f9454c | 206 | { |
506096a1 TV |
207 | struct omap_crtc *omap_crtc = omap_crtcs[mgr->id]; |
208 | ||
8472b570 | 209 | omap_crtc_set_enabled(&omap_crtc->base, false); |
f5f9454c RC |
210 | } |
211 | ||
4343f0f8 | 212 | static void omap_crtc_dss_set_timings(struct omap_overlay_manager *mgr, |
f5f9454c RC |
213 | const struct omap_video_timings *timings) |
214 | { | |
04b1fc02 | 215 | struct omap_crtc *omap_crtc = omap_crtcs[mgr->id]; |
f5f9454c RC |
216 | DBG("%s", omap_crtc->name); |
217 | omap_crtc->timings = *timings; | |
f5f9454c RC |
218 | } |
219 | ||
4343f0f8 | 220 | static void omap_crtc_dss_set_lcd_config(struct omap_overlay_manager *mgr, |
f5f9454c RC |
221 | const struct dss_lcd_mgr_config *config) |
222 | { | |
04b1fc02 | 223 | struct omap_crtc *omap_crtc = omap_crtcs[mgr->id]; |
f5f9454c RC |
224 | DBG("%s", omap_crtc->name); |
225 | dispc_mgr_set_lcd_config(omap_crtc->channel, config); | |
226 | } | |
227 | ||
4343f0f8 | 228 | static int omap_crtc_dss_register_framedone( |
f5f9454c RC |
229 | struct omap_overlay_manager *mgr, |
230 | void (*handler)(void *), void *data) | |
231 | { | |
232 | return 0; | |
233 | } | |
234 | ||
4343f0f8 | 235 | static void omap_crtc_dss_unregister_framedone( |
f5f9454c RC |
236 | struct omap_overlay_manager *mgr, |
237 | void (*handler)(void *), void *data) | |
238 | { | |
239 | } | |
240 | ||
241 | static const struct dss_mgr_ops mgr_ops = { | |
4343f0f8 LP |
242 | .connect = omap_crtc_dss_connect, |
243 | .disconnect = omap_crtc_dss_disconnect, | |
244 | .start_update = omap_crtc_dss_start_update, | |
245 | .enable = omap_crtc_dss_enable, | |
246 | .disable = omap_crtc_dss_disable, | |
247 | .set_timings = omap_crtc_dss_set_timings, | |
248 | .set_lcd_config = omap_crtc_dss_set_lcd_config, | |
249 | .register_framedone_handler = omap_crtc_dss_register_framedone, | |
250 | .unregister_framedone_handler = omap_crtc_dss_unregister_framedone, | |
cd5351f4 RC |
251 | }; |
252 | ||
971fb3e5 | 253 | /* ----------------------------------------------------------------------------- |
1d5e5ea1 | 254 | * Setup, Flush and Page Flip |
971fb3e5 LP |
255 | */ |
256 | ||
fa16d262 | 257 | static void omap_crtc_complete_page_flip(struct drm_crtc *crtc) |
15d02e92 LP |
258 | { |
259 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
fa16d262 | 260 | struct drm_pending_vblank_event *event; |
15d02e92 | 261 | struct drm_device *dev = crtc->dev; |
fa16d262 | 262 | unsigned long flags; |
15d02e92 | 263 | |
fa16d262 | 264 | spin_lock_irqsave(&dev->event_lock, flags); |
15d02e92 | 265 | |
fa16d262 LP |
266 | event = omap_crtc->event; |
267 | omap_crtc->event = NULL; | |
c397cfd4 | 268 | |
fa16d262 | 269 | if (event) { |
1cfe19aa LP |
270 | list_del(&event->base.link); |
271 | ||
272 | /* | |
273 | * Queue the event for delivery if it's still linked to a file | |
274 | * handle, otherwise just destroy it. | |
275 | */ | |
276 | if (event->base.file_priv) | |
277 | drm_crtc_send_vblank_event(crtc, event); | |
278 | else | |
279 | event->base.destroy(&event->base); | |
280 | ||
c397cfd4 | 281 | wake_up(&omap_crtc->flip_wait); |
fa16d262 LP |
282 | drm_crtc_vblank_put(crtc); |
283 | } | |
284 | ||
285 | spin_unlock_irqrestore(&dev->event_lock, flags); | |
c397cfd4 LP |
286 | } |
287 | ||
288 | static bool omap_crtc_page_flip_pending(struct drm_crtc *crtc) | |
289 | { | |
290 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
291 | struct drm_device *dev = crtc->dev; | |
292 | unsigned long flags; | |
293 | bool pending; | |
294 | ||
295 | spin_lock_irqsave(&dev->event_lock, flags); | |
fa16d262 | 296 | pending = omap_crtc->event != NULL; |
c397cfd4 LP |
297 | spin_unlock_irqrestore(&dev->event_lock, flags); |
298 | ||
299 | return pending; | |
300 | } | |
301 | ||
302 | static void omap_crtc_wait_page_flip(struct drm_crtc *crtc) | |
303 | { | |
304 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
c397cfd4 LP |
305 | |
306 | if (wait_event_timeout(omap_crtc->flip_wait, | |
307 | !omap_crtc_page_flip_pending(crtc), | |
308 | msecs_to_jiffies(50))) | |
309 | return; | |
310 | ||
311 | dev_warn(crtc->dev->dev, "page flip timeout!\n"); | |
312 | ||
fa16d262 | 313 | omap_crtc_complete_page_flip(crtc); |
15d02e92 LP |
314 | } |
315 | ||
971fb3e5 LP |
316 | static void omap_crtc_error_irq(struct omap_drm_irq *irq, uint32_t irqstatus) |
317 | { | |
318 | struct omap_crtc *omap_crtc = | |
319 | container_of(irq, struct omap_crtc, error_irq); | |
a36af73f TV |
320 | |
321 | if (omap_crtc->ignore_digit_sync_lost) { | |
322 | irqstatus &= ~DISPC_IRQ_SYNC_LOST_DIGIT; | |
323 | if (!irqstatus) | |
324 | return; | |
325 | } | |
326 | ||
3b143fc8 | 327 | DRM_ERROR_RATELIMITED("%s: errors: %08x\n", omap_crtc->name, irqstatus); |
971fb3e5 LP |
328 | } |
329 | ||
a42133a7 | 330 | static void omap_crtc_vblank_irq(struct omap_drm_irq *irq, uint32_t irqstatus) |
971fb3e5 LP |
331 | { |
332 | struct omap_crtc *omap_crtc = | |
a42133a7 LP |
333 | container_of(irq, struct omap_crtc, vblank_irq); |
334 | struct drm_device *dev = omap_crtc->base.dev; | |
971fb3e5 | 335 | |
a42133a7 LP |
336 | if (dispc_mgr_go_busy(omap_crtc->channel)) |
337 | return; | |
338 | ||
339 | DBG("%s: apply done", omap_crtc->name); | |
340 | __omap_irq_unregister(dev, &omap_crtc->vblank_irq); | |
341 | ||
a42133a7 | 342 | /* wakeup userspace */ |
fa16d262 | 343 | omap_crtc_complete_page_flip(&omap_crtc->base); |
a42133a7 LP |
344 | |
345 | complete(&omap_crtc->completion); | |
971fb3e5 LP |
346 | } |
347 | ||
bec10a2a | 348 | static int omap_crtc_flush(struct drm_crtc *crtc) |
971fb3e5 | 349 | { |
a42133a7 | 350 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); |
971fb3e5 | 351 | |
a42133a7 | 352 | DBG("%s: GO", omap_crtc->name); |
971fb3e5 | 353 | |
a42133a7 | 354 | WARN_ON(omap_crtc->vblank_irq.registered); |
971fb3e5 | 355 | |
a42133a7 LP |
356 | if (dispc_mgr_is_enabled(omap_crtc->channel)) { |
357 | dispc_mgr_go(omap_crtc->channel); | |
358 | omap_irq_register(crtc->dev, &omap_crtc->vblank_irq); | |
971fb3e5 | 359 | |
a42133a7 LP |
360 | WARN_ON(!wait_for_completion_timeout(&omap_crtc->completion, |
361 | msecs_to_jiffies(100))); | |
362 | reinit_completion(&omap_crtc->completion); | |
363 | } | |
971fb3e5 | 364 | |
971fb3e5 LP |
365 | return 0; |
366 | } | |
367 | ||
6cca481c | 368 | static void omap_crtc_encoder_setup(struct drm_crtc *crtc, bool enable) |
971fb3e5 | 369 | { |
a42133a7 | 370 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); |
971fb3e5 LP |
371 | struct omap_drm_private *priv = crtc->dev->dev_private; |
372 | struct drm_encoder *encoder = NULL; | |
373 | unsigned int i; | |
374 | ||
6cca481c | 375 | DBG("%s: enable=%d", omap_crtc->name, enable); |
971fb3e5 LP |
376 | |
377 | for (i = 0; i < priv->num_encoders; i++) { | |
378 | if (priv->encoders[i]->crtc == crtc) { | |
379 | encoder = priv->encoders[i]; | |
380 | break; | |
381 | } | |
382 | } | |
383 | ||
384 | if (omap_crtc->current_encoder && encoder != omap_crtc->current_encoder) | |
385 | omap_encoder_set_enabled(omap_crtc->current_encoder, false); | |
386 | ||
387 | omap_crtc->current_encoder = encoder; | |
388 | ||
6cca481c LP |
389 | if (encoder) { |
390 | omap_encoder_set_enabled(encoder, false); | |
391 | if (enable) { | |
971fb3e5 | 392 | omap_encoder_update(encoder, omap_crtc->mgr, |
6cca481c | 393 | &omap_crtc->timings); |
971fb3e5 LP |
394 | omap_encoder_set_enabled(encoder, true); |
395 | } | |
396 | } | |
971fb3e5 LP |
397 | } |
398 | ||
399 | /* ----------------------------------------------------------------------------- | |
400 | * CRTC Functions | |
f5f9454c RC |
401 | */ |
402 | ||
cd5351f4 RC |
403 | static void omap_crtc_destroy(struct drm_crtc *crtc) |
404 | { | |
405 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
f5f9454c RC |
406 | |
407 | DBG("%s", omap_crtc->name); | |
408 | ||
a42133a7 | 409 | WARN_ON(omap_crtc->vblank_irq.registered); |
f5f9454c RC |
410 | omap_irq_unregister(crtc->dev, &omap_crtc->error_irq); |
411 | ||
cd5351f4 | 412 | drm_crtc_cleanup(crtc); |
f5f9454c | 413 | |
cd5351f4 RC |
414 | kfree(omap_crtc); |
415 | } | |
416 | ||
f1d57fb5 LP |
417 | static bool omap_crtc_mode_fixup(struct drm_crtc *crtc, |
418 | const struct drm_display_mode *mode, | |
419 | struct drm_display_mode *adjusted_mode) | |
420 | { | |
421 | return true; | |
422 | } | |
423 | ||
424 | static void omap_crtc_enable(struct drm_crtc *crtc) | |
cd5351f4 | 425 | { |
bb5c2d9a | 426 | struct omap_drm_private *priv = crtc->dev->dev_private; |
cd5351f4 | 427 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); |
f1d57fb5 | 428 | unsigned int i; |
cd5351f4 | 429 | |
f1d57fb5 | 430 | DBG("%s", omap_crtc->name); |
f5f9454c | 431 | |
f1d57fb5 | 432 | /* Enable all planes associated with the CRTC. */ |
a42133a7 LP |
433 | for (i = 0; i < priv->num_planes; i++) { |
434 | struct drm_plane *plane = priv->planes[i]; | |
435 | ||
436 | if (plane->crtc == crtc) | |
edc72557 | 437 | WARN_ON(omap_plane_setup(plane)); |
cd5351f4 | 438 | } |
a42133a7 | 439 | |
6cca481c | 440 | omap_crtc_encoder_setup(crtc, true); |
a42133a7 | 441 | omap_crtc_flush(crtc); |
c397cfd4 | 442 | |
f1d57fb5 | 443 | drm_crtc_vblank_on(crtc); |
cd5351f4 RC |
444 | } |
445 | ||
f1d57fb5 | 446 | static void omap_crtc_disable(struct drm_crtc *crtc) |
cd5351f4 | 447 | { |
f1d57fb5 LP |
448 | struct omap_drm_private *priv = crtc->dev->dev_private; |
449 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
450 | unsigned int i; | |
451 | ||
452 | DBG("%s", omap_crtc->name); | |
453 | ||
f1d57fb5 | 454 | omap_crtc_wait_page_flip(crtc); |
f1d57fb5 | 455 | drm_crtc_vblank_off(crtc); |
f1d57fb5 LP |
456 | |
457 | /* Disable all planes associated with the CRTC. */ | |
458 | for (i = 0; i < priv->num_planes; i++) { | |
459 | struct drm_plane *plane = priv->planes[i]; | |
460 | ||
461 | if (plane->crtc == crtc) | |
edc72557 | 462 | WARN_ON(omap_plane_setup(plane)); |
f1d57fb5 LP |
463 | } |
464 | ||
6cca481c | 465 | omap_crtc_encoder_setup(crtc, false); |
f1d57fb5 | 466 | omap_crtc_flush(crtc); |
cd5351f4 RC |
467 | } |
468 | ||
f7a73b65 | 469 | static void omap_crtc_mode_set_nofb(struct drm_crtc *crtc) |
cd5351f4 RC |
470 | { |
471 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | |
f7a73b65 | 472 | struct drm_display_mode *mode = &crtc->state->adjusted_mode; |
f5f9454c RC |
473 | |
474 | DBG("%s: set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x", | |
f7a73b65 LP |
475 | omap_crtc->name, mode->base.id, mode->name, |
476 | mode->vrefresh, mode->clock, | |
477 | mode->hdisplay, mode->hsync_start, mode->hsync_end, mode->htotal, | |
478 | mode->vdisplay, mode->vsync_start, mode->vsync_end, mode->vtotal, | |
479 | mode->type, mode->flags); | |
f5f9454c RC |
480 | |
481 | copy_timings_drm_to_omap(&omap_crtc->timings, mode); | |
cd5351f4 RC |
482 | } |
483 | ||
de8e4100 LP |
484 | static void omap_crtc_atomic_begin(struct drm_crtc *crtc) |
485 | { | |
fa16d262 | 486 | struct drm_pending_vblank_event *event = crtc->state->event; |
f5f9454c | 487 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); |
cd5351f4 | 488 | struct drm_device *dev = crtc->dev; |
38e5597a | 489 | unsigned long flags; |
cd5351f4 | 490 | |
fa16d262 LP |
491 | if (event) { |
492 | WARN_ON(omap_crtc->event); | |
493 | WARN_ON(drm_crtc_vblank_get(crtc) != 0); | |
38e5597a | 494 | |
fa16d262 LP |
495 | spin_lock_irqsave(&dev->event_lock, flags); |
496 | omap_crtc->event = event; | |
38e5597a | 497 | spin_unlock_irqrestore(&dev->event_lock, flags); |
cd5351f4 | 498 | } |
fa16d262 | 499 | } |
cd5351f4 | 500 | |
fa16d262 LP |
501 | static void omap_crtc_atomic_flush(struct drm_crtc *crtc) |
502 | { | |
503 | omap_crtc_flush(crtc); | |
cd5351f4 | 504 | |
afc34932 LP |
505 | crtc->invert_dimensions = !!(crtc->primary->state->rotation & |
506 | (BIT(DRM_ROTATE_90) | BIT(DRM_ROTATE_270))); | |
cd5351f4 RC |
507 | } |
508 | ||
afc34932 LP |
509 | static int omap_crtc_atomic_set_property(struct drm_crtc *crtc, |
510 | struct drm_crtc_state *state, | |
511 | struct drm_property *property, | |
512 | uint64_t val) | |
3c810c61 | 513 | { |
afc34932 LP |
514 | struct drm_plane_state *plane_state; |
515 | struct drm_plane *plane = crtc->primary; | |
516 | ||
517 | /* | |
518 | * Delegate property set to the primary plane. Get the plane state and | |
519 | * set the property directly. | |
520 | */ | |
521 | ||
522 | plane_state = drm_atomic_get_plane_state(state->state, plane); | |
523 | if (!plane_state) | |
524 | return -EINVAL; | |
525 | ||
526 | return drm_atomic_plane_set_property(plane, plane_state, property, val); | |
527 | } | |
1e0fdfc2 | 528 | |
afc34932 LP |
529 | static int omap_crtc_atomic_get_property(struct drm_crtc *crtc, |
530 | const struct drm_crtc_state *state, | |
531 | struct drm_property *property, | |
532 | uint64_t *val) | |
533 | { | |
534 | /* | |
535 | * Delegate property get to the primary plane. The | |
536 | * drm_atomic_plane_get_property() function isn't exported, but can be | |
537 | * called through drm_object_property_get_value() as that will call | |
538 | * drm_atomic_get_property() for atomic drivers. | |
539 | */ | |
540 | return drm_object_property_get_value(&crtc->primary->base, property, | |
541 | val); | |
3c810c61 RC |
542 | } |
543 | ||
cd5351f4 | 544 | static const struct drm_crtc_funcs omap_crtc_funcs = { |
69a12263 | 545 | .reset = drm_atomic_helper_crtc_reset, |
9416c9df | 546 | .set_config = drm_atomic_helper_set_config, |
cd5351f4 | 547 | .destroy = omap_crtc_destroy, |
fa16d262 | 548 | .page_flip = drm_atomic_helper_page_flip, |
afc34932 | 549 | .set_property = drm_atomic_helper_crtc_set_property, |
69a12263 LP |
550 | .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, |
551 | .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, | |
afc34932 LP |
552 | .atomic_set_property = omap_crtc_atomic_set_property, |
553 | .atomic_get_property = omap_crtc_atomic_get_property, | |
cd5351f4 RC |
554 | }; |
555 | ||
556 | static const struct drm_crtc_helper_funcs omap_crtc_helper_funcs = { | |
cd5351f4 | 557 | .mode_fixup = omap_crtc_mode_fixup, |
f7a73b65 | 558 | .mode_set_nofb = omap_crtc_mode_set_nofb, |
f1d57fb5 LP |
559 | .disable = omap_crtc_disable, |
560 | .enable = omap_crtc_enable, | |
de8e4100 LP |
561 | .atomic_begin = omap_crtc_atomic_begin, |
562 | .atomic_flush = omap_crtc_atomic_flush, | |
cd5351f4 RC |
563 | }; |
564 | ||
971fb3e5 LP |
565 | /* ----------------------------------------------------------------------------- |
566 | * Init and Cleanup | |
567 | */ | |
e2f8fd74 | 568 | |
f5f9454c | 569 | static const char *channel_names[] = { |
222025e4 LP |
570 | [OMAP_DSS_CHANNEL_LCD] = "lcd", |
571 | [OMAP_DSS_CHANNEL_DIGIT] = "tv", | |
572 | [OMAP_DSS_CHANNEL_LCD2] = "lcd2", | |
573 | [OMAP_DSS_CHANNEL_LCD3] = "lcd3", | |
f5f9454c RC |
574 | }; |
575 | ||
04b1fc02 TV |
576 | void omap_crtc_pre_init(void) |
577 | { | |
578 | dss_install_mgr_ops(&mgr_ops); | |
579 | } | |
580 | ||
3a01ab25 AT |
581 | void omap_crtc_pre_uninit(void) |
582 | { | |
583 | dss_uninstall_mgr_ops(); | |
584 | } | |
585 | ||
cd5351f4 RC |
586 | /* initialize crtc */ |
587 | struct drm_crtc *omap_crtc_init(struct drm_device *dev, | |
f5f9454c | 588 | struct drm_plane *plane, enum omap_channel channel, int id) |
cd5351f4 RC |
589 | { |
590 | struct drm_crtc *crtc = NULL; | |
f5f9454c | 591 | struct omap_crtc *omap_crtc; |
ef6b0e02 | 592 | int ret; |
f5f9454c RC |
593 | |
594 | DBG("%s", channel_names[channel]); | |
cd5351f4 | 595 | |
f5f9454c | 596 | omap_crtc = kzalloc(sizeof(*omap_crtc), GFP_KERNEL); |
78110bb8 | 597 | if (!omap_crtc) |
ef6b0e02 | 598 | return NULL; |
cd5351f4 | 599 | |
cd5351f4 | 600 | crtc = &omap_crtc->base; |
bb5c2d9a | 601 | |
c397cfd4 | 602 | init_waitqueue_head(&omap_crtc->flip_wait); |
a42133a7 | 603 | init_completion(&omap_crtc->completion); |
f5f9454c | 604 | |
0d8f371f | 605 | omap_crtc->channel = channel; |
0d8f371f | 606 | omap_crtc->name = channel_names[channel]; |
0d8f371f | 607 | |
a42133a7 LP |
608 | omap_crtc->vblank_irq.irqmask = pipe2vbl(crtc); |
609 | omap_crtc->vblank_irq.irq = omap_crtc_vblank_irq; | |
f5f9454c RC |
610 | |
611 | omap_crtc->error_irq.irqmask = | |
612 | dispc_mgr_get_sync_lost_irq(channel); | |
613 | omap_crtc->error_irq.irq = omap_crtc_error_irq; | |
614 | omap_irq_register(dev, &omap_crtc->error_irq); | |
615 | ||
f5f9454c | 616 | /* temporary: */ |
04b1fc02 | 617 | omap_crtc->mgr = omap_dss_get_overlay_manager(channel); |
f5f9454c | 618 | |
ef6b0e02 LP |
619 | ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL, |
620 | &omap_crtc_funcs); | |
621 | if (ret < 0) { | |
622 | kfree(omap_crtc); | |
623 | return NULL; | |
624 | } | |
625 | ||
cd5351f4 RC |
626 | drm_crtc_helper_add(crtc, &omap_crtc_helper_funcs); |
627 | ||
ef6b0e02 | 628 | omap_plane_install_properties(crtc->primary, &crtc->base); |
3c810c61 | 629 | |
04b1fc02 TV |
630 | omap_crtcs[channel] = omap_crtc; |
631 | ||
cd5351f4 | 632 | return crtc; |
cd5351f4 | 633 | } |