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