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