|
@@ -20,6 +20,7 @@
|
|
|
#include "armada_hw.h"
|
|
|
|
|
|
struct armada_frame_work {
|
|
|
+ struct armada_plane_work work;
|
|
|
struct drm_pending_vblank_event *event;
|
|
|
struct armada_regs regs[4];
|
|
|
struct drm_framebuffer *old_fb;
|
|
@@ -33,6 +34,23 @@ enum csc_mode {
|
|
|
CSC_RGB_STUDIO = 2,
|
|
|
};
|
|
|
|
|
|
+static const uint32_t armada_primary_formats[] = {
|
|
|
+ DRM_FORMAT_UYVY,
|
|
|
+ DRM_FORMAT_YUYV,
|
|
|
+ DRM_FORMAT_VYUY,
|
|
|
+ DRM_FORMAT_YVYU,
|
|
|
+ DRM_FORMAT_ARGB8888,
|
|
|
+ DRM_FORMAT_ABGR8888,
|
|
|
+ DRM_FORMAT_XRGB8888,
|
|
|
+ DRM_FORMAT_XBGR8888,
|
|
|
+ DRM_FORMAT_RGB888,
|
|
|
+ DRM_FORMAT_BGR888,
|
|
|
+ DRM_FORMAT_ARGB1555,
|
|
|
+ DRM_FORMAT_ABGR1555,
|
|
|
+ DRM_FORMAT_RGB565,
|
|
|
+ DRM_FORMAT_BGR565,
|
|
|
+};
|
|
|
+
|
|
|
/*
|
|
|
* A note about interlacing. Let's consider HDMI 1920x1080i.
|
|
|
* The timing parameters we have from X are:
|
|
@@ -173,49 +191,82 @@ static unsigned armada_drm_crtc_calc_fb(struct drm_framebuffer *fb,
|
|
|
return i;
|
|
|
}
|
|
|
|
|
|
-static int armada_drm_crtc_queue_frame_work(struct armada_crtc *dcrtc,
|
|
|
- struct armada_frame_work *work)
|
|
|
+static void armada_drm_plane_work_run(struct armada_crtc *dcrtc,
|
|
|
+ struct armada_plane *plane)
|
|
|
+{
|
|
|
+ struct armada_plane_work *work = xchg(&plane->work, NULL);
|
|
|
+
|
|
|
+ /* Handle any pending frame work. */
|
|
|
+ if (work) {
|
|
|
+ work->fn(dcrtc, plane, work);
|
|
|
+ drm_vblank_put(dcrtc->crtc.dev, dcrtc->num);
|
|
|
+ }
|
|
|
+
|
|
|
+ wake_up(&plane->frame_wait);
|
|
|
+}
|
|
|
+
|
|
|
+int armada_drm_plane_work_queue(struct armada_crtc *dcrtc,
|
|
|
+ struct armada_plane *plane, struct armada_plane_work *work)
|
|
|
{
|
|
|
- struct drm_device *dev = dcrtc->crtc.dev;
|
|
|
- unsigned long flags;
|
|
|
int ret;
|
|
|
|
|
|
- ret = drm_vblank_get(dev, dcrtc->num);
|
|
|
+ ret = drm_vblank_get(dcrtc->crtc.dev, dcrtc->num);
|
|
|
if (ret) {
|
|
|
DRM_ERROR("failed to acquire vblank counter\n");
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
- spin_lock_irqsave(&dev->event_lock, flags);
|
|
|
- if (!dcrtc->frame_work)
|
|
|
- dcrtc->frame_work = work;
|
|
|
- else
|
|
|
- ret = -EBUSY;
|
|
|
- spin_unlock_irqrestore(&dev->event_lock, flags);
|
|
|
-
|
|
|
+ ret = cmpxchg(&plane->work, NULL, work) ? -EBUSY : 0;
|
|
|
if (ret)
|
|
|
- drm_vblank_put(dev, dcrtc->num);
|
|
|
+ drm_vblank_put(dcrtc->crtc.dev, dcrtc->num);
|
|
|
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
-static void armada_drm_crtc_complete_frame_work(struct armada_crtc *dcrtc)
|
|
|
+int armada_drm_plane_work_wait(struct armada_plane *plane, long timeout)
|
|
|
{
|
|
|
- struct drm_device *dev = dcrtc->crtc.dev;
|
|
|
- struct armada_frame_work *work = dcrtc->frame_work;
|
|
|
+ return wait_event_timeout(plane->frame_wait, !plane->work, timeout);
|
|
|
+}
|
|
|
|
|
|
- dcrtc->frame_work = NULL;
|
|
|
+struct armada_plane_work *armada_drm_plane_work_cancel(
|
|
|
+ struct armada_crtc *dcrtc, struct armada_plane *plane)
|
|
|
+{
|
|
|
+ struct armada_plane_work *work = xchg(&plane->work, NULL);
|
|
|
|
|
|
- armada_drm_crtc_update_regs(dcrtc, work->regs);
|
|
|
+ if (work)
|
|
|
+ drm_vblank_put(dcrtc->crtc.dev, dcrtc->num);
|
|
|
|
|
|
- if (work->event)
|
|
|
- drm_send_vblank_event(dev, dcrtc->num, work->event);
|
|
|
+ return work;
|
|
|
+}
|
|
|
|
|
|
- drm_vblank_put(dev, dcrtc->num);
|
|
|
+static int armada_drm_crtc_queue_frame_work(struct armada_crtc *dcrtc,
|
|
|
+ struct armada_frame_work *work)
|
|
|
+{
|
|
|
+ struct armada_plane *plane = drm_to_armada_plane(dcrtc->crtc.primary);
|
|
|
+
|
|
|
+ return armada_drm_plane_work_queue(dcrtc, plane, &work->work);
|
|
|
+}
|
|
|
+
|
|
|
+static void armada_drm_crtc_complete_frame_work(struct armada_crtc *dcrtc,
|
|
|
+ struct armada_plane *plane, struct armada_plane_work *work)
|
|
|
+{
|
|
|
+ struct armada_frame_work *fwork = container_of(work, struct armada_frame_work, work);
|
|
|
+ struct drm_device *dev = dcrtc->crtc.dev;
|
|
|
+ unsigned long flags;
|
|
|
+
|
|
|
+ spin_lock_irqsave(&dcrtc->irq_lock, flags);
|
|
|
+ armada_drm_crtc_update_regs(dcrtc, fwork->regs);
|
|
|
+ spin_unlock_irqrestore(&dcrtc->irq_lock, flags);
|
|
|
+
|
|
|
+ if (fwork->event) {
|
|
|
+ spin_lock_irqsave(&dev->event_lock, flags);
|
|
|
+ drm_send_vblank_event(dev, dcrtc->num, fwork->event);
|
|
|
+ spin_unlock_irqrestore(&dev->event_lock, flags);
|
|
|
+ }
|
|
|
|
|
|
/* Finally, queue the process-half of the cleanup. */
|
|
|
- __armada_drm_queue_unref_work(dcrtc->crtc.dev, work->old_fb);
|
|
|
- kfree(work);
|
|
|
+ __armada_drm_queue_unref_work(dcrtc->crtc.dev, fwork->old_fb);
|
|
|
+ kfree(fwork);
|
|
|
}
|
|
|
|
|
|
static void armada_drm_crtc_finish_fb(struct armada_crtc *dcrtc,
|
|
@@ -235,6 +286,7 @@ static void armada_drm_crtc_finish_fb(struct armada_crtc *dcrtc,
|
|
|
work = kmalloc(sizeof(*work), GFP_KERNEL);
|
|
|
if (work) {
|
|
|
int i = 0;
|
|
|
+ work->work.fn = armada_drm_crtc_complete_frame_work;
|
|
|
work->event = NULL;
|
|
|
work->old_fb = fb;
|
|
|
armada_reg_queue_end(work->regs, i);
|
|
@@ -255,19 +307,14 @@ static void armada_drm_crtc_finish_fb(struct armada_crtc *dcrtc,
|
|
|
|
|
|
static void armada_drm_vblank_off(struct armada_crtc *dcrtc)
|
|
|
{
|
|
|
- struct drm_device *dev = dcrtc->crtc.dev;
|
|
|
+ struct armada_plane *plane = drm_to_armada_plane(dcrtc->crtc.primary);
|
|
|
|
|
|
/*
|
|
|
* Tell the DRM core that vblank IRQs aren't going to happen for
|
|
|
* a while. This cleans up any pending vblank events for us.
|
|
|
*/
|
|
|
drm_crtc_vblank_off(&dcrtc->crtc);
|
|
|
-
|
|
|
- /* Handle any pending flip event. */
|
|
|
- spin_lock_irq(&dev->event_lock);
|
|
|
- if (dcrtc->frame_work)
|
|
|
- armada_drm_crtc_complete_frame_work(dcrtc);
|
|
|
- spin_unlock_irq(&dev->event_lock);
|
|
|
+ armada_drm_plane_work_run(dcrtc, plane);
|
|
|
}
|
|
|
|
|
|
void armada_drm_crtc_gamma_set(struct drm_crtc *crtc, u16 r, u16 g, u16 b,
|
|
@@ -287,7 +334,11 @@ static void armada_drm_crtc_dpms(struct drm_crtc *crtc, int dpms)
|
|
|
|
|
|
if (dcrtc->dpms != dpms) {
|
|
|
dcrtc->dpms = dpms;
|
|
|
+ if (!IS_ERR(dcrtc->clk) && !dpms_blanked(dpms))
|
|
|
+ WARN_ON(clk_prepare_enable(dcrtc->clk));
|
|
|
armada_drm_crtc_update(dcrtc);
|
|
|
+ if (!IS_ERR(dcrtc->clk) && dpms_blanked(dpms))
|
|
|
+ clk_disable_unprepare(dcrtc->clk);
|
|
|
if (dpms_blanked(dpms))
|
|
|
armada_drm_vblank_off(dcrtc);
|
|
|
else
|
|
@@ -310,17 +361,11 @@ static void armada_drm_crtc_prepare(struct drm_crtc *crtc)
|
|
|
/*
|
|
|
* If we have an overlay plane associated with this CRTC, disable
|
|
|
* it before the modeset to avoid its coordinates being outside
|
|
|
- * the new mode parameters. DRM doesn't provide help with this.
|
|
|
+ * the new mode parameters.
|
|
|
*/
|
|
|
plane = dcrtc->plane;
|
|
|
- if (plane) {
|
|
|
- struct drm_framebuffer *fb = plane->fb;
|
|
|
-
|
|
|
- plane->funcs->disable_plane(plane);
|
|
|
- plane->fb = NULL;
|
|
|
- plane->crtc = NULL;
|
|
|
- drm_framebuffer_unreference(fb);
|
|
|
- }
|
|
|
+ if (plane)
|
|
|
+ drm_plane_force_disable(plane);
|
|
|
}
|
|
|
|
|
|
/* The mode_config.mutex will be held for this call */
|
|
@@ -356,8 +401,8 @@ static bool armada_drm_crtc_mode_fixup(struct drm_crtc *crtc,
|
|
|
|
|
|
static void armada_drm_crtc_irq(struct armada_crtc *dcrtc, u32 stat)
|
|
|
{
|
|
|
- struct armada_vbl_event *e, *n;
|
|
|
void __iomem *base = dcrtc->base;
|
|
|
+ struct drm_plane *ovl_plane;
|
|
|
|
|
|
if (stat & DMA_FF_UNDERFLOW)
|
|
|
DRM_ERROR("video underflow on crtc %u\n", dcrtc->num);
|
|
@@ -368,11 +413,10 @@ static void armada_drm_crtc_irq(struct armada_crtc *dcrtc, u32 stat)
|
|
|
drm_handle_vblank(dcrtc->crtc.dev, dcrtc->num);
|
|
|
|
|
|
spin_lock(&dcrtc->irq_lock);
|
|
|
-
|
|
|
- list_for_each_entry_safe(e, n, &dcrtc->vbl_list, node) {
|
|
|
- list_del_init(&e->node);
|
|
|
- drm_vblank_put(dcrtc->crtc.dev, dcrtc->num);
|
|
|
- e->fn(dcrtc, e->data);
|
|
|
+ ovl_plane = dcrtc->plane;
|
|
|
+ if (ovl_plane) {
|
|
|
+ struct armada_plane *plane = drm_to_armada_plane(ovl_plane);
|
|
|
+ armada_drm_plane_work_run(dcrtc, plane);
|
|
|
}
|
|
|
|
|
|
if (stat & GRA_FRAME_IRQ && dcrtc->interlaced) {
|
|
@@ -404,14 +448,8 @@ static void armada_drm_crtc_irq(struct armada_crtc *dcrtc, u32 stat)
|
|
|
spin_unlock(&dcrtc->irq_lock);
|
|
|
|
|
|
if (stat & GRA_FRAME_IRQ) {
|
|
|
- struct drm_device *dev = dcrtc->crtc.dev;
|
|
|
-
|
|
|
- spin_lock(&dev->event_lock);
|
|
|
- if (dcrtc->frame_work)
|
|
|
- armada_drm_crtc_complete_frame_work(dcrtc);
|
|
|
- spin_unlock(&dev->event_lock);
|
|
|
-
|
|
|
- wake_up(&dcrtc->frame_wait);
|
|
|
+ struct armada_plane *plane = drm_to_armada_plane(dcrtc->crtc.primary);
|
|
|
+ armada_drm_plane_work_run(dcrtc, plane);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -527,7 +565,8 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc,
|
|
|
adj->crtc_vtotal, tm, bm);
|
|
|
|
|
|
/* Wait for pending flips to complete */
|
|
|
- wait_event(dcrtc->frame_wait, !dcrtc->frame_work);
|
|
|
+ armada_drm_plane_work_wait(drm_to_armada_plane(dcrtc->crtc.primary),
|
|
|
+ MAX_SCHEDULE_TIMEOUT);
|
|
|
|
|
|
drm_crtc_vblank_off(crtc);
|
|
|
|
|
@@ -537,6 +576,13 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc,
|
|
|
writel_relaxed(val, dcrtc->base + LCD_SPU_DUMB_CTRL);
|
|
|
}
|
|
|
|
|
|
+ /*
|
|
|
+ * If we are blanked, we would have disabled the clock. Re-enable
|
|
|
+ * it so that compute_clock() does the right thing.
|
|
|
+ */
|
|
|
+ if (!IS_ERR(dcrtc->clk) && dpms_blanked(dcrtc->dpms))
|
|
|
+ WARN_ON(clk_prepare_enable(dcrtc->clk));
|
|
|
+
|
|
|
/* Now compute the divider for real */
|
|
|
dcrtc->variant->compute_clock(dcrtc, adj, &sclk);
|
|
|
|
|
@@ -637,7 +683,8 @@ static int armada_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
|
|
|
armada_reg_queue_end(regs, i);
|
|
|
|
|
|
/* Wait for pending flips to complete */
|
|
|
- wait_event(dcrtc->frame_wait, !dcrtc->frame_work);
|
|
|
+ armada_drm_plane_work_wait(drm_to_armada_plane(dcrtc->crtc.primary),
|
|
|
+ MAX_SCHEDULE_TIMEOUT);
|
|
|
|
|
|
/* Take a reference to the new fb as we're using it */
|
|
|
drm_framebuffer_reference(crtc->primary->fb);
|
|
@@ -651,18 +698,47 @@ static int armada_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+void armada_drm_crtc_plane_disable(struct armada_crtc *dcrtc,
|
|
|
+ struct drm_plane *plane)
|
|
|
+{
|
|
|
+ u32 sram_para1, dma_ctrl0_mask;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Drop our reference on any framebuffer attached to this plane.
|
|
|
+ * We don't need to NULL this out as drm_plane_force_disable(),
|
|
|
+ * and __setplane_internal() will do so for an overlay plane, and
|
|
|
+ * __drm_helper_disable_unused_functions() will do so for the
|
|
|
+ * primary plane.
|
|
|
+ */
|
|
|
+ if (plane->fb)
|
|
|
+ drm_framebuffer_unreference(plane->fb);
|
|
|
+
|
|
|
+ /* Power down the Y/U/V FIFOs */
|
|
|
+ sram_para1 = CFG_PDWN16x66 | CFG_PDWN32x66;
|
|
|
+
|
|
|
+ /* Power down most RAMs and FIFOs if this is the primary plane */
|
|
|
+ if (plane->type == DRM_PLANE_TYPE_PRIMARY) {
|
|
|
+ sram_para1 |= CFG_PDWN256x32 | CFG_PDWN256x24 | CFG_PDWN256x8 |
|
|
|
+ CFG_PDWN32x32 | CFG_PDWN64x66;
|
|
|
+ dma_ctrl0_mask = CFG_GRA_ENA;
|
|
|
+ } else {
|
|
|
+ dma_ctrl0_mask = CFG_DMA_ENA;
|
|
|
+ }
|
|
|
+
|
|
|
+ spin_lock_irq(&dcrtc->irq_lock);
|
|
|
+ armada_updatel(0, dma_ctrl0_mask, dcrtc->base + LCD_SPU_DMA_CTRL0);
|
|
|
+ spin_unlock_irq(&dcrtc->irq_lock);
|
|
|
+
|
|
|
+ armada_updatel(sram_para1, 0, dcrtc->base + LCD_SPU_SRAM_PARA1);
|
|
|
+}
|
|
|
+
|
|
|
/* The mode_config.mutex will be held for this call */
|
|
|
static void armada_drm_crtc_disable(struct drm_crtc *crtc)
|
|
|
{
|
|
|
struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
|
|
|
|
|
|
armada_drm_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
|
|
|
- armada_drm_crtc_finish_fb(dcrtc, crtc->primary->fb, true);
|
|
|
-
|
|
|
- /* Power down most RAMs and FIFOs */
|
|
|
- writel_relaxed(CFG_PDWN256x32 | CFG_PDWN256x24 | CFG_PDWN256x8 |
|
|
|
- CFG_PDWN32x32 | CFG_PDWN16x66 | CFG_PDWN32x66 |
|
|
|
- CFG_PDWN64x66, dcrtc->base + LCD_SPU_SRAM_PARA1);
|
|
|
+ armada_drm_crtc_plane_disable(dcrtc, crtc->primary);
|
|
|
}
|
|
|
|
|
|
static const struct drm_crtc_helper_funcs armada_crtc_helper_funcs = {
|
|
@@ -920,8 +996,6 @@ static int armada_drm_crtc_page_flip(struct drm_crtc *crtc,
|
|
|
{
|
|
|
struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
|
|
|
struct armada_frame_work *work;
|
|
|
- struct drm_device *dev = crtc->dev;
|
|
|
- unsigned long flags;
|
|
|
unsigned i;
|
|
|
int ret;
|
|
|
|
|
@@ -933,6 +1007,7 @@ static int armada_drm_crtc_page_flip(struct drm_crtc *crtc,
|
|
|
if (!work)
|
|
|
return -ENOMEM;
|
|
|
|
|
|
+ work->work.fn = armada_drm_crtc_complete_frame_work;
|
|
|
work->event = event;
|
|
|
work->old_fb = dcrtc->crtc.primary->fb;
|
|
|
|
|
@@ -966,12 +1041,8 @@ static int armada_drm_crtc_page_flip(struct drm_crtc *crtc,
|
|
|
* Finally, if the display is blanked, we won't receive an
|
|
|
* interrupt, so complete it now.
|
|
|
*/
|
|
|
- if (dpms_blanked(dcrtc->dpms)) {
|
|
|
- spin_lock_irqsave(&dev->event_lock, flags);
|
|
|
- if (dcrtc->frame_work)
|
|
|
- armada_drm_crtc_complete_frame_work(dcrtc);
|
|
|
- spin_unlock_irqrestore(&dev->event_lock, flags);
|
|
|
- }
|
|
|
+ if (dpms_blanked(dcrtc->dpms))
|
|
|
+ armada_drm_plane_work_run(dcrtc, drm_to_armada_plane(dcrtc->crtc.primary));
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
@@ -1012,6 +1083,19 @@ static struct drm_crtc_funcs armada_crtc_funcs = {
|
|
|
.set_property = armada_drm_crtc_set_property,
|
|
|
};
|
|
|
|
|
|
+static const struct drm_plane_funcs armada_primary_plane_funcs = {
|
|
|
+ .update_plane = drm_primary_helper_update,
|
|
|
+ .disable_plane = drm_primary_helper_disable,
|
|
|
+ .destroy = drm_primary_helper_destroy,
|
|
|
+};
|
|
|
+
|
|
|
+int armada_drm_plane_init(struct armada_plane *plane)
|
|
|
+{
|
|
|
+ init_waitqueue_head(&plane->frame_wait);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static struct drm_prop_enum_list armada_drm_csc_yuv_enum_list[] = {
|
|
|
{ CSC_AUTO, "Auto" },
|
|
|
{ CSC_YUV_CCIR601, "CCIR601" },
|
|
@@ -1044,12 +1128,13 @@ static int armada_drm_crtc_create_properties(struct drm_device *dev)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-int armada_drm_crtc_create(struct drm_device *drm, struct device *dev,
|
|
|
+static int armada_drm_crtc_create(struct drm_device *drm, struct device *dev,
|
|
|
struct resource *res, int irq, const struct armada_variant *variant,
|
|
|
struct device_node *port)
|
|
|
{
|
|
|
struct armada_private *priv = drm->dev_private;
|
|
|
struct armada_crtc *dcrtc;
|
|
|
+ struct armada_plane *primary;
|
|
|
void __iomem *base;
|
|
|
int ret;
|
|
|
|
|
@@ -1080,8 +1165,6 @@ int armada_drm_crtc_create(struct drm_device *drm, struct device *dev,
|
|
|
dcrtc->spu_iopad_ctrl = CFG_VSCALE_LN_EN | CFG_IOPAD_DUMB24;
|
|
|
spin_lock_init(&dcrtc->irq_lock);
|
|
|
dcrtc->irq_ena = CLEAN_SPU_IRQ_ISR;
|
|
|
- INIT_LIST_HEAD(&dcrtc->vbl_list);
|
|
|
- init_waitqueue_head(&dcrtc->frame_wait);
|
|
|
|
|
|
/* Initialize some registers which we don't otherwise set */
|
|
|
writel_relaxed(0x00000001, dcrtc->base + LCD_CFG_SCLK_DIV);
|
|
@@ -1118,7 +1201,32 @@ int armada_drm_crtc_create(struct drm_device *drm, struct device *dev,
|
|
|
priv->dcrtc[dcrtc->num] = dcrtc;
|
|
|
|
|
|
dcrtc->crtc.port = port;
|
|
|
- drm_crtc_init(drm, &dcrtc->crtc, &armada_crtc_funcs);
|
|
|
+
|
|
|
+ primary = kzalloc(sizeof(*primary), GFP_KERNEL);
|
|
|
+ if (!primary)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ ret = armada_drm_plane_init(primary);
|
|
|
+ if (ret) {
|
|
|
+ kfree(primary);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = drm_universal_plane_init(drm, &primary->base, 0,
|
|
|
+ &armada_primary_plane_funcs,
|
|
|
+ armada_primary_formats,
|
|
|
+ ARRAY_SIZE(armada_primary_formats),
|
|
|
+ DRM_PLANE_TYPE_PRIMARY);
|
|
|
+ if (ret) {
|
|
|
+ kfree(primary);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = drm_crtc_init_with_planes(drm, &dcrtc->crtc, &primary->base, NULL,
|
|
|
+ &armada_crtc_funcs);
|
|
|
+ if (ret)
|
|
|
+ goto err_crtc_init;
|
|
|
+
|
|
|
drm_crtc_helper_add(&dcrtc->crtc, &armada_crtc_helper_funcs);
|
|
|
|
|
|
drm_object_attach_property(&dcrtc->crtc.base, priv->csc_yuv_prop,
|
|
@@ -1127,6 +1235,10 @@ int armada_drm_crtc_create(struct drm_device *drm, struct device *dev,
|
|
|
dcrtc->csc_rgb_mode);
|
|
|
|
|
|
return armada_overlay_plane_create(drm, 1 << dcrtc->num);
|
|
|
+
|
|
|
+err_crtc_init:
|
|
|
+ primary->base.funcs->destroy(&primary->base);
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
static int
|