Merge branch 'drm-armada-devel' of git://ftp.arm.linux.org.uk/~rmk/linux-arm into drm-next
* remove support for the non-component support from the Armada DRM driver, switching it to component-only mode. * create a "armada plane" to allow the primary and overlay planes to share some code. * increase efficiency by using inherently atomic operations, rather than spinlocking to achieve atomicity. Eg, if we want to exchange a value, using xchg(). * increase PM savings by stopping the external pixel clock when we're in DPMS mode. * 'drm-armada-devel' of git://ftp.arm.linux.org.uk/~rmk/linux-arm: drm/armada: move frame wait wakeup into plane work drm/armada: convert overlay plane vbl worker to a armada plane worker drm/armada: move CRTC flip work to primary plane work drm/armada: move frame wait into armada_frame drm/armada: move the locking for armada_drm_vbl_event_remove() drm/armada: move the update of dplane->ctrl0 out of spinlock drm/armada: move write to dma_ctrl0 to armada_drm_crtc_plane_disable() drm/armada: provide a common helper to disable a plane drm/armada: allocate primary plane ourselves drm/armada: add primary plane creation drm/armada: introduce generic armada_plane struct drm/armada: update armada overlay to use drm_universal_plane_init() drm/armada: use xchg() to atomically update dplane->old_fb drm/armada: factor out retirement of old fb drm/armada: rename overlay identifiers drm/armada: redo locking and atomics for armada_drm_crtc_complete_frame_work() drm/armada: disable CRTC clock during DPMS drm/armada: use drm_plane_force_disable() to disable the overlay plane drm/armada: move vbl code into armada_crtc drm/armada: remove non-component support
This commit is contained in:
Коммит
bbbe29d8e0
|
@ -14,12 +14,3 @@ config DRM_ARMADA
|
|||
This driver provides no built-in acceleration; acceleration is
|
||||
performed by other IP found on the SoC. This driver provides
|
||||
kernel mode setting and buffer management to userspace.
|
||||
|
||||
config DRM_ARMADA_TDA1998X
|
||||
bool "Support TDA1998X HDMI output"
|
||||
depends on DRM_ARMADA != n
|
||||
depends on I2C && DRM_I2C_NXP_TDA998X = y
|
||||
default y
|
||||
help
|
||||
Support the TDA1998x HDMI output device found on the Solid-Run
|
||||
CuBox.
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
armada-y := armada_crtc.o armada_drv.o armada_fb.o armada_fbdev.o \
|
||||
armada_gem.o armada_output.o armada_overlay.o \
|
||||
armada_slave.o
|
||||
armada_gem.o armada_overlay.o
|
||||
armada-y += armada_510.o
|
||||
armada-$(CONFIG_DEBUG_FS) += armada_debugfs.o
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
return wait_event_timeout(plane->frame_wait, !plane->work, timeout);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (work)
|
||||
drm_vblank_put(dcrtc->crtc.dev, dcrtc->num);
|
||||
|
||||
return work;
|
||||
}
|
||||
|
||||
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;
|
||||
struct armada_frame_work *work = dcrtc->frame_work;
|
||||
unsigned long flags;
|
||||
|
||||
dcrtc->frame_work = NULL;
|
||||
spin_lock_irqsave(&dcrtc->irq_lock, flags);
|
||||
armada_drm_crtc_update_regs(dcrtc, fwork->regs);
|
||||
spin_unlock_irqrestore(&dcrtc->irq_lock, flags);
|
||||
|
||||
armada_drm_crtc_update_regs(dcrtc, work->regs);
|
||||
|
||||
if (work->event)
|
||||
drm_send_vblank_event(dev, dcrtc->num, work->event);
|
||||
|
||||
drm_vblank_put(dev, dcrtc->num);
|
||||
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
|
||||
|
|
|
@ -31,9 +31,30 @@ struct armada_regs {
|
|||
#define armada_reg_queue_end(_r, _i) \
|
||||
armada_reg_queue_mod(_r, _i, 0, 0, ~0)
|
||||
|
||||
struct armada_frame_work;
|
||||
struct armada_crtc;
|
||||
struct armada_plane;
|
||||
struct armada_variant;
|
||||
|
||||
struct armada_plane_work {
|
||||
void (*fn)(struct armada_crtc *,
|
||||
struct armada_plane *,
|
||||
struct armada_plane_work *);
|
||||
};
|
||||
|
||||
struct armada_plane {
|
||||
struct drm_plane base;
|
||||
wait_queue_head_t frame_wait;
|
||||
struct armada_plane_work *work;
|
||||
};
|
||||
#define drm_to_armada_plane(p) container_of(p, struct armada_plane, base)
|
||||
|
||||
int armada_drm_plane_init(struct armada_plane *plane);
|
||||
int armada_drm_plane_work_queue(struct armada_crtc *dcrtc,
|
||||
struct armada_plane *plane, struct armada_plane_work *work);
|
||||
int armada_drm_plane_work_wait(struct armada_plane *plane, long timeout);
|
||||
struct armada_plane_work *armada_drm_plane_work_cancel(
|
||||
struct armada_crtc *dcrtc, struct armada_plane *plane);
|
||||
|
||||
struct armada_crtc {
|
||||
struct drm_crtc crtc;
|
||||
const struct armada_variant *variant;
|
||||
|
@ -66,25 +87,20 @@ struct armada_crtc {
|
|||
uint32_t dumb_ctrl;
|
||||
uint32_t spu_iopad_ctrl;
|
||||
|
||||
wait_queue_head_t frame_wait;
|
||||
struct armada_frame_work *frame_work;
|
||||
|
||||
spinlock_t irq_lock;
|
||||
uint32_t irq_ena;
|
||||
struct list_head vbl_list;
|
||||
};
|
||||
#define drm_to_armada_crtc(c) container_of(c, struct armada_crtc, crtc)
|
||||
|
||||
struct device_node;
|
||||
int armada_drm_crtc_create(struct drm_device *, struct device *,
|
||||
struct resource *, int, const struct armada_variant *,
|
||||
struct device_node *);
|
||||
void armada_drm_crtc_gamma_set(struct drm_crtc *, u16, u16, u16, int);
|
||||
void armada_drm_crtc_gamma_get(struct drm_crtc *, u16 *, u16 *, u16 *, int);
|
||||
void armada_drm_crtc_disable_irq(struct armada_crtc *, u32);
|
||||
void armada_drm_crtc_enable_irq(struct armada_crtc *, u32);
|
||||
void armada_drm_crtc_update_regs(struct armada_crtc *, struct armada_regs *);
|
||||
|
||||
void armada_drm_crtc_plane_disable(struct armada_crtc *dcrtc,
|
||||
struct drm_plane *plane);
|
||||
|
||||
extern struct platform_driver armada_lcd_platform_driver;
|
||||
|
||||
#endif
|
||||
|
|
|
@ -37,22 +37,6 @@ static inline uint32_t armada_pitch(uint32_t width, uint32_t bpp)
|
|||
return ALIGN(pitch, 128);
|
||||
}
|
||||
|
||||
struct armada_vbl_event {
|
||||
struct list_head node;
|
||||
void *data;
|
||||
void (*fn)(struct armada_crtc *, void *);
|
||||
};
|
||||
void armada_drm_vbl_event_add(struct armada_crtc *,
|
||||
struct armada_vbl_event *);
|
||||
void armada_drm_vbl_event_remove(struct armada_crtc *,
|
||||
struct armada_vbl_event *);
|
||||
#define armada_drm_vbl_event_init(_e, _f, _d) do { \
|
||||
struct armada_vbl_event *__e = _e; \
|
||||
INIT_LIST_HEAD(&__e->node); \
|
||||
__e->data = _d; \
|
||||
__e->fn = _f; \
|
||||
} while (0)
|
||||
|
||||
|
||||
struct armada_private;
|
||||
|
||||
|
|
|
@ -18,47 +18,6 @@
|
|||
#include <drm/armada_drm.h>
|
||||
#include "armada_ioctlP.h"
|
||||
|
||||
#ifdef CONFIG_DRM_ARMADA_TDA1998X
|
||||
#include <drm/i2c/tda998x.h>
|
||||
#include "armada_slave.h"
|
||||
|
||||
static struct tda998x_encoder_params params = {
|
||||
/* With 0x24, there is no translation between vp_out and int_vp
|
||||
FB LCD out Pins VIP Int Vp
|
||||
R:23:16 R:7:0 VPC7:0 7:0 7:0[R]
|
||||
G:15:8 G:15:8 VPB7:0 23:16 23:16[G]
|
||||
B:7:0 B:23:16 VPA7:0 15:8 15:8[B]
|
||||
*/
|
||||
.swap_a = 2,
|
||||
.swap_b = 3,
|
||||
.swap_c = 4,
|
||||
.swap_d = 5,
|
||||
.swap_e = 0,
|
||||
.swap_f = 1,
|
||||
.audio_cfg = BIT(2),
|
||||
.audio_frame[1] = 1,
|
||||
.audio_format = AFMT_SPDIF,
|
||||
.audio_sample_rate = 44100,
|
||||
};
|
||||
|
||||
static const struct armada_drm_slave_config tda19988_config = {
|
||||
.i2c_adapter_id = 0,
|
||||
.crtcs = 1 << 0, /* Only LCD0 at the moment */
|
||||
.polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT,
|
||||
.interlace_allowed = true,
|
||||
.info = {
|
||||
.type = "tda998x",
|
||||
.addr = 0x70,
|
||||
.platform_data = ¶ms,
|
||||
},
|
||||
};
|
||||
#endif
|
||||
|
||||
static bool is_componentized(struct device *dev)
|
||||
{
|
||||
return dev->of_node || dev->platform_data;
|
||||
}
|
||||
|
||||
static void armada_drm_unref_work(struct work_struct *work)
|
||||
{
|
||||
struct armada_private *priv =
|
||||
|
@ -91,16 +50,11 @@ void armada_drm_queue_unref_work(struct drm_device *dev,
|
|||
|
||||
static int armada_drm_load(struct drm_device *dev, unsigned long flags)
|
||||
{
|
||||
const struct platform_device_id *id;
|
||||
const struct armada_variant *variant;
|
||||
struct armada_private *priv;
|
||||
struct resource *res[ARRAY_SIZE(priv->dcrtc)];
|
||||
struct resource *mem = NULL;
|
||||
int ret, n, i;
|
||||
int ret, n;
|
||||
|
||||
memset(res, 0, sizeof(res));
|
||||
|
||||
for (n = i = 0; ; n++) {
|
||||
for (n = 0; ; n++) {
|
||||
struct resource *r = platform_get_resource(dev->platformdev,
|
||||
IORESOURCE_MEM, n);
|
||||
if (!r)
|
||||
|
@ -109,8 +63,6 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags)
|
|||
/* Resources above 64K are graphics memory */
|
||||
if (resource_size(r) > SZ_64K)
|
||||
mem = r;
|
||||
else if (i < ARRAY_SIZE(priv->dcrtc))
|
||||
res[i++] = r;
|
||||
else
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -131,13 +83,6 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags)
|
|||
platform_set_drvdata(dev->platformdev, dev);
|
||||
dev->dev_private = priv;
|
||||
|
||||
/* Get the implementation specific driver data. */
|
||||
id = platform_get_device_id(dev->platformdev);
|
||||
if (!id)
|
||||
return -ENXIO;
|
||||
|
||||
variant = (const struct armada_variant *)id->driver_data;
|
||||
|
||||
INIT_WORK(&priv->fb_unref_work, armada_drm_unref_work);
|
||||
INIT_KFIFO(priv->fb_unref);
|
||||
|
||||
|
@ -157,34 +102,9 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags)
|
|||
dev->mode_config.funcs = &armada_drm_mode_config_funcs;
|
||||
drm_mm_init(&priv->linear, mem->start, resource_size(mem));
|
||||
|
||||
/* Create all LCD controllers */
|
||||
for (n = 0; n < ARRAY_SIZE(priv->dcrtc); n++) {
|
||||
int irq;
|
||||
|
||||
if (!res[n])
|
||||
break;
|
||||
|
||||
irq = platform_get_irq(dev->platformdev, n);
|
||||
if (irq < 0)
|
||||
goto err_kms;
|
||||
|
||||
ret = armada_drm_crtc_create(dev, dev->dev, res[n], irq,
|
||||
variant, NULL);
|
||||
if (ret)
|
||||
goto err_kms;
|
||||
}
|
||||
|
||||
if (is_componentized(dev->dev)) {
|
||||
ret = component_bind_all(dev->dev, dev);
|
||||
if (ret)
|
||||
goto err_kms;
|
||||
} else {
|
||||
#ifdef CONFIG_DRM_ARMADA_TDA1998X
|
||||
ret = armada_drm_connector_slave_create(dev, &tda19988_config);
|
||||
if (ret)
|
||||
goto err_kms;
|
||||
#endif
|
||||
}
|
||||
ret = component_bind_all(dev->dev, dev);
|
||||
if (ret)
|
||||
goto err_kms;
|
||||
|
||||
ret = drm_vblank_init(dev, dev->mode_config.num_crtc);
|
||||
if (ret)
|
||||
|
@ -202,8 +122,7 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags)
|
|||
return 0;
|
||||
|
||||
err_comp:
|
||||
if (is_componentized(dev->dev))
|
||||
component_unbind_all(dev->dev, dev);
|
||||
component_unbind_all(dev->dev, dev);
|
||||
err_kms:
|
||||
drm_mode_config_cleanup(dev);
|
||||
drm_mm_takedown(&priv->linear);
|
||||
|
@ -219,8 +138,7 @@ static int armada_drm_unload(struct drm_device *dev)
|
|||
drm_kms_helper_poll_fini(dev);
|
||||
armada_fbdev_fini(dev);
|
||||
|
||||
if (is_componentized(dev->dev))
|
||||
component_unbind_all(dev->dev, dev);
|
||||
component_unbind_all(dev->dev, dev);
|
||||
|
||||
drm_mode_config_cleanup(dev);
|
||||
drm_mm_takedown(&priv->linear);
|
||||
|
@ -230,29 +148,6 @@ static int armada_drm_unload(struct drm_device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
void armada_drm_vbl_event_add(struct armada_crtc *dcrtc,
|
||||
struct armada_vbl_event *evt)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&dcrtc->irq_lock, flags);
|
||||
if (list_empty(&evt->node)) {
|
||||
list_add_tail(&evt->node, &dcrtc->vbl_list);
|
||||
|
||||
drm_vblank_get(dcrtc->crtc.dev, dcrtc->num);
|
||||
}
|
||||
spin_unlock_irqrestore(&dcrtc->irq_lock, flags);
|
||||
}
|
||||
|
||||
void armada_drm_vbl_event_remove(struct armada_crtc *dcrtc,
|
||||
struct armada_vbl_event *evt)
|
||||
{
|
||||
if (!list_empty(&evt->node)) {
|
||||
list_del_init(&evt->node);
|
||||
drm_vblank_put(dcrtc->crtc.dev, dcrtc->num);
|
||||
}
|
||||
}
|
||||
|
||||
/* These are called under the vbl_lock. */
|
||||
static int armada_drm_enable_vblank(struct drm_device *dev, int crtc)
|
||||
{
|
||||
|
@ -435,37 +330,28 @@ static const struct component_master_ops armada_master_ops = {
|
|||
|
||||
static int armada_drm_probe(struct platform_device *pdev)
|
||||
{
|
||||
if (is_componentized(&pdev->dev)) {
|
||||
struct component_match *match = NULL;
|
||||
int ret;
|
||||
struct component_match *match = NULL;
|
||||
int ret;
|
||||
|
||||
ret = armada_drm_find_components(&pdev->dev, &match);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = armada_drm_find_components(&pdev->dev, &match);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return component_master_add_with_match(&pdev->dev,
|
||||
&armada_master_ops, match);
|
||||
} else {
|
||||
return drm_platform_init(&armada_drm_driver, pdev);
|
||||
}
|
||||
return component_master_add_with_match(&pdev->dev, &armada_master_ops,
|
||||
match);
|
||||
}
|
||||
|
||||
static int armada_drm_remove(struct platform_device *pdev)
|
||||
{
|
||||
if (is_componentized(&pdev->dev))
|
||||
component_master_del(&pdev->dev, &armada_master_ops);
|
||||
else
|
||||
drm_put_dev(platform_get_drvdata(pdev));
|
||||
component_master_del(&pdev->dev, &armada_master_ops);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct platform_device_id armada_drm_platform_ids[] = {
|
||||
{
|
||||
.name = "armada-drm",
|
||||
.driver_data = (unsigned long)&armada510_ops,
|
||||
}, {
|
||||
.name = "armada-510-drm",
|
||||
.driver_data = (unsigned long)&armada510_ops,
|
||||
},
|
||||
{ },
|
||||
};
|
||||
|
|
|
@ -1,142 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2012 Russell King
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_edid.h>
|
||||
#include <drm/drm_encoder_slave.h>
|
||||
#include "armada_output.h"
|
||||
#include "armada_drm.h"
|
||||
|
||||
struct armada_connector {
|
||||
struct drm_connector conn;
|
||||
const struct armada_output_type *type;
|
||||
};
|
||||
|
||||
#define drm_to_armada_conn(c) container_of(c, struct armada_connector, conn)
|
||||
|
||||
struct drm_encoder *armada_drm_connector_encoder(struct drm_connector *conn)
|
||||
{
|
||||
struct drm_encoder *enc = conn->encoder;
|
||||
|
||||
return enc ? enc : drm_encoder_find(conn->dev, conn->encoder_ids[0]);
|
||||
}
|
||||
|
||||
static enum drm_connector_status armada_drm_connector_detect(
|
||||
struct drm_connector *conn, bool force)
|
||||
{
|
||||
struct armada_connector *dconn = drm_to_armada_conn(conn);
|
||||
enum drm_connector_status status = connector_status_disconnected;
|
||||
|
||||
if (dconn->type->detect) {
|
||||
status = dconn->type->detect(conn, force);
|
||||
} else {
|
||||
struct drm_encoder *enc = armada_drm_connector_encoder(conn);
|
||||
|
||||
if (enc)
|
||||
status = encoder_helper_funcs(enc)->detect(enc, conn);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static void armada_drm_connector_destroy(struct drm_connector *conn)
|
||||
{
|
||||
struct armada_connector *dconn = drm_to_armada_conn(conn);
|
||||
|
||||
drm_connector_unregister(conn);
|
||||
drm_connector_cleanup(conn);
|
||||
kfree(dconn);
|
||||
}
|
||||
|
||||
static int armada_drm_connector_set_property(struct drm_connector *conn,
|
||||
struct drm_property *property, uint64_t value)
|
||||
{
|
||||
struct armada_connector *dconn = drm_to_armada_conn(conn);
|
||||
|
||||
if (!dconn->type->set_property)
|
||||
return -EINVAL;
|
||||
|
||||
return dconn->type->set_property(conn, property, value);
|
||||
}
|
||||
|
||||
static const struct drm_connector_funcs armada_drm_conn_funcs = {
|
||||
.dpms = drm_helper_connector_dpms,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.detect = armada_drm_connector_detect,
|
||||
.destroy = armada_drm_connector_destroy,
|
||||
.set_property = armada_drm_connector_set_property,
|
||||
};
|
||||
|
||||
/* Shouldn't this be a generic helper function? */
|
||||
int armada_drm_slave_encoder_mode_valid(struct drm_connector *conn,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
struct drm_encoder *encoder = armada_drm_connector_encoder(conn);
|
||||
int valid = MODE_BAD;
|
||||
|
||||
if (encoder) {
|
||||
struct drm_encoder_slave *slave = to_encoder_slave(encoder);
|
||||
|
||||
valid = slave->slave_funcs->mode_valid(encoder, mode);
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
|
||||
int armada_drm_slave_encoder_set_property(struct drm_connector *conn,
|
||||
struct drm_property *property, uint64_t value)
|
||||
{
|
||||
struct drm_encoder *encoder = armada_drm_connector_encoder(conn);
|
||||
int rc = -EINVAL;
|
||||
|
||||
if (encoder) {
|
||||
struct drm_encoder_slave *slave = to_encoder_slave(encoder);
|
||||
|
||||
rc = slave->slave_funcs->set_property(encoder, conn, property,
|
||||
value);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
int armada_output_create(struct drm_device *dev,
|
||||
const struct armada_output_type *type, const void *data)
|
||||
{
|
||||
struct armada_connector *dconn;
|
||||
int ret;
|
||||
|
||||
dconn = kzalloc(sizeof(*dconn), GFP_KERNEL);
|
||||
if (!dconn)
|
||||
return -ENOMEM;
|
||||
|
||||
dconn->type = type;
|
||||
|
||||
ret = drm_connector_init(dev, &dconn->conn, &armada_drm_conn_funcs,
|
||||
type->connector_type);
|
||||
if (ret) {
|
||||
DRM_ERROR("unable to init connector\n");
|
||||
goto err_destroy_dconn;
|
||||
}
|
||||
|
||||
ret = type->create(&dconn->conn, data);
|
||||
if (ret)
|
||||
goto err_conn;
|
||||
|
||||
ret = drm_connector_register(&dconn->conn);
|
||||
if (ret)
|
||||
goto err_sysfs;
|
||||
|
||||
return 0;
|
||||
|
||||
err_sysfs:
|
||||
if (dconn->conn.encoder)
|
||||
dconn->conn.encoder->funcs->destroy(dconn->conn.encoder);
|
||||
err_conn:
|
||||
drm_connector_cleanup(&dconn->conn);
|
||||
err_destroy_dconn:
|
||||
kfree(dconn);
|
||||
return ret;
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2012 Russell King
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#ifndef ARMADA_CONNETOR_H
|
||||
#define ARMADA_CONNETOR_H
|
||||
|
||||
#define encoder_helper_funcs(encoder) \
|
||||
((const struct drm_encoder_helper_funcs *)encoder->helper_private)
|
||||
|
||||
struct armada_output_type {
|
||||
int connector_type;
|
||||
enum drm_connector_status (*detect)(struct drm_connector *, bool);
|
||||
int (*create)(struct drm_connector *, const void *);
|
||||
int (*set_property)(struct drm_connector *, struct drm_property *,
|
||||
uint64_t);
|
||||
};
|
||||
|
||||
struct drm_encoder *armada_drm_connector_encoder(struct drm_connector *conn);
|
||||
|
||||
int armada_drm_slave_encoder_mode_valid(struct drm_connector *conn,
|
||||
struct drm_display_mode *mode);
|
||||
|
||||
int armada_drm_slave_encoder_set_property(struct drm_connector *conn,
|
||||
struct drm_property *property, uint64_t value);
|
||||
|
||||
int armada_output_create(struct drm_device *dev,
|
||||
const struct armada_output_type *type, const void *data);
|
||||
|
||||
#endif
|
|
@ -16,7 +16,7 @@
|
|||
#include <drm/armada_drm.h>
|
||||
#include "armada_ioctlP.h"
|
||||
|
||||
struct armada_plane_properties {
|
||||
struct armada_ovl_plane_properties {
|
||||
uint32_t colorkey_yr;
|
||||
uint32_t colorkey_ug;
|
||||
uint32_t colorkey_vb;
|
||||
|
@ -29,26 +29,25 @@ struct armada_plane_properties {
|
|||
uint32_t colorkey_mode;
|
||||
};
|
||||
|
||||
struct armada_plane {
|
||||
struct drm_plane base;
|
||||
spinlock_t lock;
|
||||
struct armada_ovl_plane {
|
||||
struct armada_plane base;
|
||||
struct drm_framebuffer *old_fb;
|
||||
uint32_t src_hw;
|
||||
uint32_t dst_hw;
|
||||
uint32_t dst_yx;
|
||||
uint32_t ctrl0;
|
||||
struct {
|
||||
struct armada_vbl_event update;
|
||||
struct armada_plane_work work;
|
||||
struct armada_regs regs[13];
|
||||
wait_queue_head_t wait;
|
||||
} vbl;
|
||||
struct armada_plane_properties prop;
|
||||
struct armada_ovl_plane_properties prop;
|
||||
};
|
||||
#define drm_to_armada_plane(p) container_of(p, struct armada_plane, base)
|
||||
#define drm_to_armada_ovl_plane(p) \
|
||||
container_of(p, struct armada_ovl_plane, base.base)
|
||||
|
||||
|
||||
static void
|
||||
armada_ovl_update_attr(struct armada_plane_properties *prop,
|
||||
armada_ovl_update_attr(struct armada_ovl_plane_properties *prop,
|
||||
struct armada_crtc *dcrtc)
|
||||
{
|
||||
writel_relaxed(prop->colorkey_yr, dcrtc->base + LCD_SPU_COLORKEY_Y);
|
||||
|
@ -71,32 +70,34 @@ armada_ovl_update_attr(struct armada_plane_properties *prop,
|
|||
spin_unlock_irq(&dcrtc->irq_lock);
|
||||
}
|
||||
|
||||
/* === Plane support === */
|
||||
static void armada_plane_vbl(struct armada_crtc *dcrtc, void *data)
|
||||
static void armada_ovl_retire_fb(struct armada_ovl_plane *dplane,
|
||||
struct drm_framebuffer *fb)
|
||||
{
|
||||
struct armada_plane *dplane = data;
|
||||
struct drm_framebuffer *fb;
|
||||
struct drm_framebuffer *old_fb;
|
||||
|
||||
old_fb = xchg(&dplane->old_fb, fb);
|
||||
|
||||
if (old_fb)
|
||||
armada_drm_queue_unref_work(dplane->base.base.dev, old_fb);
|
||||
}
|
||||
|
||||
/* === Plane support === */
|
||||
static void armada_ovl_plane_work(struct armada_crtc *dcrtc,
|
||||
struct armada_plane *plane, struct armada_plane_work *work)
|
||||
{
|
||||
struct armada_ovl_plane *dplane = container_of(plane, struct armada_ovl_plane, base);
|
||||
|
||||
armada_drm_crtc_update_regs(dcrtc, dplane->vbl.regs);
|
||||
|
||||
spin_lock(&dplane->lock);
|
||||
fb = dplane->old_fb;
|
||||
dplane->old_fb = NULL;
|
||||
spin_unlock(&dplane->lock);
|
||||
|
||||
if (fb)
|
||||
armada_drm_queue_unref_work(dcrtc->crtc.dev, fb);
|
||||
|
||||
wake_up(&dplane->vbl.wait);
|
||||
armada_ovl_retire_fb(dplane, NULL);
|
||||
}
|
||||
|
||||
static int
|
||||
armada_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
|
||||
armada_ovl_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
|
||||
struct drm_framebuffer *fb,
|
||||
int crtc_x, int crtc_y, unsigned crtc_w, unsigned crtc_h,
|
||||
uint32_t src_x, uint32_t src_y, uint32_t src_w, uint32_t src_h)
|
||||
{
|
||||
struct armada_plane *dplane = drm_to_armada_plane(plane);
|
||||
struct armada_ovl_plane *dplane = drm_to_armada_ovl_plane(plane);
|
||||
struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
|
||||
struct drm_rect src = {
|
||||
.x1 = src_x,
|
||||
|
@ -160,9 +161,8 @@ armada_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
|
|||
dcrtc->base + LCD_SPU_SRAM_PARA1);
|
||||
}
|
||||
|
||||
wait_event_timeout(dplane->vbl.wait,
|
||||
list_empty(&dplane->vbl.update.node),
|
||||
HZ/25);
|
||||
if (armada_drm_plane_work_wait(&dplane->base, HZ / 25) == 0)
|
||||
armada_drm_plane_work_cancel(dcrtc, &dplane->base);
|
||||
|
||||
if (plane->fb != fb) {
|
||||
struct armada_gem_object *obj = drm_fb_obj(fb);
|
||||
|
@ -175,17 +175,8 @@ armada_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
|
|||
*/
|
||||
drm_framebuffer_reference(fb);
|
||||
|
||||
if (plane->fb) {
|
||||
struct drm_framebuffer *older_fb;
|
||||
|
||||
spin_lock_irq(&dplane->lock);
|
||||
older_fb = dplane->old_fb;
|
||||
dplane->old_fb = plane->fb;
|
||||
spin_unlock_irq(&dplane->lock);
|
||||
if (older_fb)
|
||||
armada_drm_queue_unref_work(dcrtc->crtc.dev,
|
||||
older_fb);
|
||||
}
|
||||
if (plane->fb)
|
||||
armada_ovl_retire_fb(dplane, plane->fb);
|
||||
|
||||
src_y = src.y1 >> 16;
|
||||
src_x = src.x1 >> 16;
|
||||
|
@ -262,60 +253,50 @@ armada_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
|
|||
}
|
||||
if (idx) {
|
||||
armada_reg_queue_end(dplane->vbl.regs, idx);
|
||||
armada_drm_vbl_event_add(dcrtc, &dplane->vbl.update);
|
||||
armada_drm_plane_work_queue(dcrtc, &dplane->base,
|
||||
&dplane->vbl.work);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int armada_plane_disable(struct drm_plane *plane)
|
||||
static int armada_ovl_plane_disable(struct drm_plane *plane)
|
||||
{
|
||||
struct armada_plane *dplane = drm_to_armada_plane(plane);
|
||||
struct armada_ovl_plane *dplane = drm_to_armada_ovl_plane(plane);
|
||||
struct drm_framebuffer *fb;
|
||||
struct armada_crtc *dcrtc;
|
||||
|
||||
if (!dplane->base.crtc)
|
||||
if (!dplane->base.base.crtc)
|
||||
return 0;
|
||||
|
||||
dcrtc = drm_to_armada_crtc(dplane->base.crtc);
|
||||
dcrtc = drm_to_armada_crtc(dplane->base.base.crtc);
|
||||
|
||||
armada_drm_plane_work_cancel(dcrtc, &dplane->base);
|
||||
armada_drm_crtc_plane_disable(dcrtc, plane);
|
||||
|
||||
dcrtc->plane = NULL;
|
||||
|
||||
spin_lock_irq(&dcrtc->irq_lock);
|
||||
armada_drm_vbl_event_remove(dcrtc, &dplane->vbl.update);
|
||||
armada_updatel(0, CFG_DMA_ENA, dcrtc->base + LCD_SPU_DMA_CTRL0);
|
||||
dplane->ctrl0 = 0;
|
||||
spin_unlock_irq(&dcrtc->irq_lock);
|
||||
|
||||
/* Power down the Y/U/V FIFOs */
|
||||
armada_updatel(CFG_PDWN16x66 | CFG_PDWN32x66, 0,
|
||||
dcrtc->base + LCD_SPU_SRAM_PARA1);
|
||||
|
||||
if (plane->fb)
|
||||
drm_framebuffer_unreference(plane->fb);
|
||||
|
||||
spin_lock_irq(&dplane->lock);
|
||||
fb = dplane->old_fb;
|
||||
dplane->old_fb = NULL;
|
||||
spin_unlock_irq(&dplane->lock);
|
||||
fb = xchg(&dplane->old_fb, NULL);
|
||||
if (fb)
|
||||
drm_framebuffer_unreference(fb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void armada_plane_destroy(struct drm_plane *plane)
|
||||
static void armada_ovl_plane_destroy(struct drm_plane *plane)
|
||||
{
|
||||
struct armada_plane *dplane = drm_to_armada_plane(plane);
|
||||
struct armada_ovl_plane *dplane = drm_to_armada_ovl_plane(plane);
|
||||
|
||||
drm_plane_cleanup(plane);
|
||||
|
||||
kfree(dplane);
|
||||
}
|
||||
|
||||
static int armada_plane_set_property(struct drm_plane *plane,
|
||||
static int armada_ovl_plane_set_property(struct drm_plane *plane,
|
||||
struct drm_property *property, uint64_t val)
|
||||
{
|
||||
struct armada_private *priv = plane->dev->dev_private;
|
||||
struct armada_plane *dplane = drm_to_armada_plane(plane);
|
||||
struct armada_ovl_plane *dplane = drm_to_armada_ovl_plane(plane);
|
||||
bool update_attr = false;
|
||||
|
||||
if (property == priv->colorkey_prop) {
|
||||
|
@ -372,21 +353,21 @@ static int armada_plane_set_property(struct drm_plane *plane,
|
|||
update_attr = true;
|
||||
}
|
||||
|
||||
if (update_attr && dplane->base.crtc)
|
||||
if (update_attr && dplane->base.base.crtc)
|
||||
armada_ovl_update_attr(&dplane->prop,
|
||||
drm_to_armada_crtc(dplane->base.crtc));
|
||||
drm_to_armada_crtc(dplane->base.base.crtc));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct drm_plane_funcs armada_plane_funcs = {
|
||||
.update_plane = armada_plane_update,
|
||||
.disable_plane = armada_plane_disable,
|
||||
.destroy = armada_plane_destroy,
|
||||
.set_property = armada_plane_set_property,
|
||||
static const struct drm_plane_funcs armada_ovl_plane_funcs = {
|
||||
.update_plane = armada_ovl_plane_update,
|
||||
.disable_plane = armada_ovl_plane_disable,
|
||||
.destroy = armada_ovl_plane_destroy,
|
||||
.set_property = armada_ovl_plane_set_property,
|
||||
};
|
||||
|
||||
static const uint32_t armada_formats[] = {
|
||||
static const uint32_t armada_ovl_formats[] = {
|
||||
DRM_FORMAT_UYVY,
|
||||
DRM_FORMAT_YUYV,
|
||||
DRM_FORMAT_YUV420,
|
||||
|
@ -456,7 +437,7 @@ int armada_overlay_plane_create(struct drm_device *dev, unsigned long crtcs)
|
|||
{
|
||||
struct armada_private *priv = dev->dev_private;
|
||||
struct drm_mode_object *mobj;
|
||||
struct armada_plane *dplane;
|
||||
struct armada_ovl_plane *dplane;
|
||||
int ret;
|
||||
|
||||
ret = armada_overlay_create_properties(dev);
|
||||
|
@ -467,13 +448,23 @@ int armada_overlay_plane_create(struct drm_device *dev, unsigned long crtcs)
|
|||
if (!dplane)
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_init(&dplane->lock);
|
||||
init_waitqueue_head(&dplane->vbl.wait);
|
||||
armada_drm_vbl_event_init(&dplane->vbl.update, armada_plane_vbl,
|
||||
dplane);
|
||||
ret = armada_drm_plane_init(&dplane->base);
|
||||
if (ret) {
|
||||
kfree(dplane);
|
||||
return ret;
|
||||
}
|
||||
|
||||
drm_plane_init(dev, &dplane->base, crtcs, &armada_plane_funcs,
|
||||
armada_formats, ARRAY_SIZE(armada_formats), false);
|
||||
dplane->vbl.work.fn = armada_ovl_plane_work;
|
||||
|
||||
ret = drm_universal_plane_init(dev, &dplane->base.base, crtcs,
|
||||
&armada_ovl_plane_funcs,
|
||||
armada_ovl_formats,
|
||||
ARRAY_SIZE(armada_ovl_formats),
|
||||
DRM_PLANE_TYPE_OVERLAY);
|
||||
if (ret) {
|
||||
kfree(dplane);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dplane->prop.colorkey_yr = 0xfefefe00;
|
||||
dplane->prop.colorkey_ug = 0x01010100;
|
||||
|
@ -483,7 +474,7 @@ int armada_overlay_plane_create(struct drm_device *dev, unsigned long crtcs)
|
|||
dplane->prop.contrast = 0x4000;
|
||||
dplane->prop.saturation = 0x4000;
|
||||
|
||||
mobj = &dplane->base.base;
|
||||
mobj = &dplane->base.base.base;
|
||||
drm_object_attach_property(mobj, priv->colorkey_prop,
|
||||
0x0101fe);
|
||||
drm_object_attach_property(mobj, priv->colorkey_min_prop,
|
||||
|
|
|
@ -1,139 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2012 Russell King
|
||||
* Rewritten from the dovefb driver, and Armada510 manuals.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_edid.h>
|
||||
#include <drm/drm_encoder_slave.h>
|
||||
#include "armada_drm.h"
|
||||
#include "armada_output.h"
|
||||
#include "armada_slave.h"
|
||||
|
||||
static int armada_drm_slave_get_modes(struct drm_connector *conn)
|
||||
{
|
||||
struct drm_encoder *enc = armada_drm_connector_encoder(conn);
|
||||
int count = 0;
|
||||
|
||||
if (enc) {
|
||||
struct drm_encoder_slave *slave = to_encoder_slave(enc);
|
||||
|
||||
count = slave->slave_funcs->get_modes(enc, conn);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static void armada_drm_slave_destroy(struct drm_encoder *enc)
|
||||
{
|
||||
struct drm_encoder_slave *slave = to_encoder_slave(enc);
|
||||
struct i2c_client *client = drm_i2c_encoder_get_client(enc);
|
||||
|
||||
if (slave->slave_funcs)
|
||||
slave->slave_funcs->destroy(enc);
|
||||
if (client)
|
||||
i2c_put_adapter(client->adapter);
|
||||
|
||||
drm_encoder_cleanup(&slave->base);
|
||||
kfree(slave);
|
||||
}
|
||||
|
||||
static const struct drm_encoder_funcs armada_drm_slave_encoder_funcs = {
|
||||
.destroy = armada_drm_slave_destroy,
|
||||
};
|
||||
|
||||
static const struct drm_connector_helper_funcs armada_drm_slave_helper_funcs = {
|
||||
.get_modes = armada_drm_slave_get_modes,
|
||||
.mode_valid = armada_drm_slave_encoder_mode_valid,
|
||||
.best_encoder = armada_drm_connector_encoder,
|
||||
};
|
||||
|
||||
static const struct drm_encoder_helper_funcs drm_slave_encoder_helpers = {
|
||||
.dpms = drm_i2c_encoder_dpms,
|
||||
.save = drm_i2c_encoder_save,
|
||||
.restore = drm_i2c_encoder_restore,
|
||||
.mode_fixup = drm_i2c_encoder_mode_fixup,
|
||||
.prepare = drm_i2c_encoder_prepare,
|
||||
.commit = drm_i2c_encoder_commit,
|
||||
.mode_set = drm_i2c_encoder_mode_set,
|
||||
.detect = drm_i2c_encoder_detect,
|
||||
};
|
||||
|
||||
static int
|
||||
armada_drm_conn_slave_create(struct drm_connector *conn, const void *data)
|
||||
{
|
||||
const struct armada_drm_slave_config *config = data;
|
||||
struct drm_encoder_slave *slave;
|
||||
struct i2c_adapter *adap;
|
||||
int ret;
|
||||
|
||||
conn->interlace_allowed = config->interlace_allowed;
|
||||
conn->doublescan_allowed = config->doublescan_allowed;
|
||||
conn->polled = config->polled;
|
||||
|
||||
drm_connector_helper_add(conn, &armada_drm_slave_helper_funcs);
|
||||
|
||||
slave = kzalloc(sizeof(*slave), GFP_KERNEL);
|
||||
if (!slave)
|
||||
return -ENOMEM;
|
||||
|
||||
slave->base.possible_crtcs = config->crtcs;
|
||||
|
||||
adap = i2c_get_adapter(config->i2c_adapter_id);
|
||||
if (!adap) {
|
||||
kfree(slave);
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
ret = drm_encoder_init(conn->dev, &slave->base,
|
||||
&armada_drm_slave_encoder_funcs,
|
||||
DRM_MODE_ENCODER_TMDS);
|
||||
if (ret) {
|
||||
DRM_ERROR("unable to init encoder\n");
|
||||
i2c_put_adapter(adap);
|
||||
kfree(slave);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = drm_i2c_encoder_init(conn->dev, slave, adap, &config->info);
|
||||
i2c_put_adapter(adap);
|
||||
if (ret) {
|
||||
DRM_ERROR("unable to init encoder slave\n");
|
||||
armada_drm_slave_destroy(&slave->base);
|
||||
return ret;
|
||||
}
|
||||
|
||||
drm_encoder_helper_add(&slave->base, &drm_slave_encoder_helpers);
|
||||
|
||||
ret = slave->slave_funcs->create_resources(&slave->base, conn);
|
||||
if (ret) {
|
||||
armada_drm_slave_destroy(&slave->base);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = drm_mode_connector_attach_encoder(conn, &slave->base);
|
||||
if (ret) {
|
||||
armada_drm_slave_destroy(&slave->base);
|
||||
return ret;
|
||||
}
|
||||
|
||||
conn->encoder = &slave->base;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct armada_output_type armada_drm_conn_slave = {
|
||||
.connector_type = DRM_MODE_CONNECTOR_HDMIA,
|
||||
.create = armada_drm_conn_slave_create,
|
||||
.set_property = armada_drm_slave_encoder_set_property,
|
||||
};
|
||||
|
||||
int armada_drm_connector_slave_create(struct drm_device *dev,
|
||||
const struct armada_drm_slave_config *config)
|
||||
{
|
||||
return armada_output_create(dev, &armada_drm_conn_slave, config);
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2012 Russell King
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#ifndef ARMADA_SLAVE_H
|
||||
#define ARMADA_SLAVE_H
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <drm/drmP.h>
|
||||
|
||||
struct armada_drm_slave_config {
|
||||
int i2c_adapter_id;
|
||||
uint32_t crtcs;
|
||||
uint8_t polled;
|
||||
bool interlace_allowed;
|
||||
bool doublescan_allowed;
|
||||
struct i2c_board_info info;
|
||||
};
|
||||
|
||||
int armada_drm_connector_slave_create(struct drm_device *dev,
|
||||
const struct armada_drm_slave_config *);
|
||||
|
||||
#endif
|
Загрузка…
Ссылка в новой задаче