Commit bbbe29d8 authored by Dave Airlie's avatar Dave Airlie

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
parents d4070ff7 7cb410cd
...@@ -14,12 +14,3 @@ config DRM_ARMADA ...@@ -14,12 +14,3 @@ config DRM_ARMADA
This driver provides no built-in acceleration; acceleration is This driver provides no built-in acceleration; acceleration is
performed by other IP found on the SoC. This driver provides performed by other IP found on the SoC. This driver provides
kernel mode setting and buffer management to userspace. 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.
armada-y := armada_crtc.o armada_drv.o armada_fb.o armada_fbdev.o \ armada-y := armada_crtc.o armada_drv.o armada_fb.o armada_fbdev.o \
armada_gem.o armada_output.o armada_overlay.o \ armada_gem.o armada_overlay.o
armada_slave.o
armada-y += armada_510.o armada-y += armada_510.o
armada-$(CONFIG_DEBUG_FS) += armada_debugfs.o armada-$(CONFIG_DEBUG_FS) += armada_debugfs.o
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include "armada_hw.h" #include "armada_hw.h"
struct armada_frame_work { struct armada_frame_work {
struct armada_plane_work work;
struct drm_pending_vblank_event *event; struct drm_pending_vblank_event *event;
struct armada_regs regs[4]; struct armada_regs regs[4];
struct drm_framebuffer *old_fb; struct drm_framebuffer *old_fb;
...@@ -33,6 +34,23 @@ enum csc_mode { ...@@ -33,6 +34,23 @@ enum csc_mode {
CSC_RGB_STUDIO = 2, 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. * A note about interlacing. Let's consider HDMI 1920x1080i.
* The timing parameters we have from X are: * The timing parameters we have from X are:
...@@ -173,49 +191,82 @@ static unsigned armada_drm_crtc_calc_fb(struct drm_framebuffer *fb, ...@@ -173,49 +191,82 @@ static unsigned armada_drm_crtc_calc_fb(struct drm_framebuffer *fb,
return i; return i;
} }
static int armada_drm_crtc_queue_frame_work(struct armada_crtc *dcrtc, static void armada_drm_plane_work_run(struct armada_crtc *dcrtc,
struct armada_frame_work *work) 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; int ret;
ret = drm_vblank_get(dev, dcrtc->num); ret = drm_vblank_get(dcrtc->crtc.dev, dcrtc->num);
if (ret) { if (ret) {
DRM_ERROR("failed to acquire vblank counter\n"); DRM_ERROR("failed to acquire vblank counter\n");
return ret; return ret;
} }
spin_lock_irqsave(&dev->event_lock, flags); ret = cmpxchg(&plane->work, NULL, work) ? -EBUSY : 0;
if (!dcrtc->frame_work)
dcrtc->frame_work = work;
else
ret = -EBUSY;
spin_unlock_irqrestore(&dev->event_lock, flags);
if (ret) if (ret)
drm_vblank_put(dev, dcrtc->num); drm_vblank_put(dcrtc->crtc.dev, dcrtc->num);
return ret; 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; return wait_event_timeout(plane->frame_wait, !plane->work, timeout);
struct armada_frame_work *work = dcrtc->frame_work; }
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) return work;
drm_send_vblank_event(dev, dcrtc->num, work->event); }
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. */ /* Finally, queue the process-half of the cleanup. */
__armada_drm_queue_unref_work(dcrtc->crtc.dev, work->old_fb); __armada_drm_queue_unref_work(dcrtc->crtc.dev, fwork->old_fb);
kfree(work); kfree(fwork);
} }
static void armada_drm_crtc_finish_fb(struct armada_crtc *dcrtc, 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, ...@@ -235,6 +286,7 @@ static void armada_drm_crtc_finish_fb(struct armada_crtc *dcrtc,
work = kmalloc(sizeof(*work), GFP_KERNEL); work = kmalloc(sizeof(*work), GFP_KERNEL);
if (work) { if (work) {
int i = 0; int i = 0;
work->work.fn = armada_drm_crtc_complete_frame_work;
work->event = NULL; work->event = NULL;
work->old_fb = fb; work->old_fb = fb;
armada_reg_queue_end(work->regs, i); armada_reg_queue_end(work->regs, i);
...@@ -255,19 +307,14 @@ static void armada_drm_crtc_finish_fb(struct armada_crtc *dcrtc, ...@@ -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) 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 * Tell the DRM core that vblank IRQs aren't going to happen for
* a while. This cleans up any pending vblank events for us. * a while. This cleans up any pending vblank events for us.
*/ */
drm_crtc_vblank_off(&dcrtc->crtc); drm_crtc_vblank_off(&dcrtc->crtc);
armada_drm_plane_work_run(dcrtc, plane);
/* 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);
} }
void armada_drm_crtc_gamma_set(struct drm_crtc *crtc, u16 r, u16 g, u16 b, 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) ...@@ -287,7 +334,11 @@ static void armada_drm_crtc_dpms(struct drm_crtc *crtc, int dpms)
if (dcrtc->dpms != dpms) { if (dcrtc->dpms != dpms) {
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); armada_drm_crtc_update(dcrtc);
if (!IS_ERR(dcrtc->clk) && dpms_blanked(dpms))
clk_disable_unprepare(dcrtc->clk);
if (dpms_blanked(dpms)) if (dpms_blanked(dpms))
armada_drm_vblank_off(dcrtc); armada_drm_vblank_off(dcrtc);
else else
...@@ -310,17 +361,11 @@ static void armada_drm_crtc_prepare(struct drm_crtc *crtc) ...@@ -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 * If we have an overlay plane associated with this CRTC, disable
* it before the modeset to avoid its coordinates being outside * 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; plane = dcrtc->plane;
if (plane) { if (plane)
struct drm_framebuffer *fb = plane->fb; drm_plane_force_disable(plane);
plane->funcs->disable_plane(plane);
plane->fb = NULL;
plane->crtc = NULL;
drm_framebuffer_unreference(fb);
}
} }
/* The mode_config.mutex will be held for this call */ /* 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, ...@@ -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) static void armada_drm_crtc_irq(struct armada_crtc *dcrtc, u32 stat)
{ {
struct armada_vbl_event *e, *n;
void __iomem *base = dcrtc->base; void __iomem *base = dcrtc->base;
struct drm_plane *ovl_plane;
if (stat & DMA_FF_UNDERFLOW) if (stat & DMA_FF_UNDERFLOW)
DRM_ERROR("video underflow on crtc %u\n", dcrtc->num); 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) ...@@ -368,11 +413,10 @@ static void armada_drm_crtc_irq(struct armada_crtc *dcrtc, u32 stat)
drm_handle_vblank(dcrtc->crtc.dev, dcrtc->num); drm_handle_vblank(dcrtc->crtc.dev, dcrtc->num);
spin_lock(&dcrtc->irq_lock); spin_lock(&dcrtc->irq_lock);
ovl_plane = dcrtc->plane;
list_for_each_entry_safe(e, n, &dcrtc->vbl_list, node) { if (ovl_plane) {
list_del_init(&e->node); struct armada_plane *plane = drm_to_armada_plane(ovl_plane);
drm_vblank_put(dcrtc->crtc.dev, dcrtc->num); armada_drm_plane_work_run(dcrtc, plane);
e->fn(dcrtc, e->data);
} }
if (stat & GRA_FRAME_IRQ && dcrtc->interlaced) { if (stat & GRA_FRAME_IRQ && dcrtc->interlaced) {
...@@ -404,14 +448,8 @@ static void armada_drm_crtc_irq(struct armada_crtc *dcrtc, u32 stat) ...@@ -404,14 +448,8 @@ static void armada_drm_crtc_irq(struct armada_crtc *dcrtc, u32 stat)
spin_unlock(&dcrtc->irq_lock); spin_unlock(&dcrtc->irq_lock);
if (stat & GRA_FRAME_IRQ) { if (stat & GRA_FRAME_IRQ) {
struct drm_device *dev = dcrtc->crtc.dev; struct armada_plane *plane = drm_to_armada_plane(dcrtc->crtc.primary);
armada_drm_plane_work_run(dcrtc, plane);
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);
} }
} }
...@@ -527,7 +565,8 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc, ...@@ -527,7 +565,8 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc,
adj->crtc_vtotal, tm, bm); adj->crtc_vtotal, tm, bm);
/* Wait for pending flips to complete */ /* 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); drm_crtc_vblank_off(crtc);
...@@ -537,6 +576,13 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *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); 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 */ /* Now compute the divider for real */
dcrtc->variant->compute_clock(dcrtc, adj, &sclk); 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, ...@@ -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); armada_reg_queue_end(regs, i);
/* Wait for pending flips to complete */ /* 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 */ /* Take a reference to the new fb as we're using it */
drm_framebuffer_reference(crtc->primary->fb); 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, ...@@ -651,18 +698,47 @@ static int armada_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
return 0; 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 */ /* The mode_config.mutex will be held for this call */
static void armada_drm_crtc_disable(struct drm_crtc *crtc) static void armada_drm_crtc_disable(struct drm_crtc *crtc)
{ {
struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
armada_drm_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); armada_drm_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
armada_drm_crtc_finish_fb(dcrtc, crtc->primary->fb, true); armada_drm_crtc_plane_disable(dcrtc, crtc->primary);
/* 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);
} }
static const struct drm_crtc_helper_funcs armada_crtc_helper_funcs = { 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, ...@@ -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_crtc *dcrtc = drm_to_armada_crtc(crtc);
struct armada_frame_work *work; struct armada_frame_work *work;
struct drm_device *dev = crtc->dev;
unsigned long flags;
unsigned i; unsigned i;
int ret; int ret;
...@@ -933,6 +1007,7 @@ static int armada_drm_crtc_page_flip(struct drm_crtc *crtc, ...@@ -933,6 +1007,7 @@ static int armada_drm_crtc_page_flip(struct drm_crtc *crtc,
if (!work) if (!work)
return -ENOMEM; return -ENOMEM;
work->work.fn = armada_drm_crtc_complete_frame_work;
work->event = event; work->event = event;
work->old_fb = dcrtc->crtc.primary->fb; work->old_fb = dcrtc->crtc.primary->fb;
...@@ -966,12 +1041,8 @@ static int armada_drm_crtc_page_flip(struct drm_crtc *crtc, ...@@ -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 * Finally, if the display is blanked, we won't receive an
* interrupt, so complete it now. * interrupt, so complete it now.
*/ */
if (dpms_blanked(dcrtc->dpms)) { if (dpms_blanked(dcrtc->dpms))
spin_lock_irqsave(&dev->event_lock, flags); armada_drm_plane_work_run(dcrtc, drm_to_armada_plane(dcrtc->crtc.primary));
if (dcrtc->frame_work)
armada_drm_crtc_complete_frame_work(dcrtc);
spin_unlock_irqrestore(&dev->event_lock, flags);
}
return 0; return 0;
} }
...@@ -1012,6 +1083,19 @@ static struct drm_crtc_funcs armada_crtc_funcs = { ...@@ -1012,6 +1083,19 @@ static struct drm_crtc_funcs armada_crtc_funcs = {
.set_property = armada_drm_crtc_set_property, .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[] = { static struct drm_prop_enum_list armada_drm_csc_yuv_enum_list[] = {
{ CSC_AUTO, "Auto" }, { CSC_AUTO, "Auto" },
{ CSC_YUV_CCIR601, "CCIR601" }, { CSC_YUV_CCIR601, "CCIR601" },
...@@ -1044,12 +1128,13 @@ static int armada_drm_crtc_create_properties(struct drm_device *dev) ...@@ -1044,12 +1128,13 @@ static int armada_drm_crtc_create_properties(struct drm_device *dev)
return 0; 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 resource *res, int irq, const struct armada_variant *variant,
struct device_node *port) struct device_node *port)
{ {
struct armada_private *priv = drm->dev_private; struct armada_private *priv = drm->dev_private;
struct armada_crtc *dcrtc; struct armada_crtc *dcrtc;
struct armada_plane *primary;
void __iomem *base; void __iomem *base;
int ret; int ret;
...@@ -1080,8 +1165,6 @@ int armada_drm_crtc_create(struct drm_device *drm, struct device *dev, ...@@ -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; dcrtc->spu_iopad_ctrl = CFG_VSCALE_LN_EN | CFG_IOPAD_DUMB24;
spin_lock_init(&dcrtc->irq_lock); spin_lock_init(&dcrtc->irq_lock);
dcrtc->irq_ena = CLEAN_SPU_IRQ_ISR; 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 */ /* Initialize some registers which we don't otherwise set */
writel_relaxed(0x00000001, dcrtc->base + LCD_CFG_SCLK_DIV); 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, ...@@ -1118,7 +1201,32 @@ int armada_drm_crtc_create(struct drm_device *drm, struct device *dev,
priv->dcrtc[dcrtc->num] = dcrtc; priv->dcrtc[dcrtc->num] = dcrtc;
dcrtc->crtc.port = port; 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_crtc_helper_add(&dcrtc->crtc, &armada_crtc_helper_funcs);
drm_object_attach_property(&dcrtc->crtc.base, priv->csc_yuv_prop, 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, ...@@ -1127,6 +1235,10 @@ int armada_drm_crtc_create(struct drm_device *drm, struct device *dev,
dcrtc->csc_rgb_mode); dcrtc->csc_rgb_mode);
return armada_overlay_plane_create(drm, 1 << dcrtc->num); return armada_overlay_plane_create(drm, 1 << dcrtc->num);
err_crtc_init:
primary->base.funcs->destroy(&primary->base);
return ret;
} }
static int static int
......
...@@ -31,9 +31,30 @@ struct armada_regs { ...@@ -31,9 +31,30 @@ struct armada_regs {
#define armada_reg_queue_end(_r, _i) \ #define armada_reg_queue_end(_r, _i) \
armada_reg_queue_mod(_r, _i, 0, 0, ~0) armada_reg_queue_mod(_r, _i, 0, 0, ~0)
struct armada_frame_work; struct armada_crtc;
struct armada_plane;
struct armada_variant; 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 armada_crtc {
struct drm_crtc crtc; struct drm_crtc crtc;
const struct armada_variant *variant; const struct armada_variant *variant;
...@@ -66,25 +87,20 @@ struct armada_crtc { ...@@ -66,25 +87,20 @@ struct armada_crtc {
uint32_t dumb_ctrl; uint32_t dumb_ctrl;
uint32_t spu_iopad_ctrl; uint32_t spu_iopad_ctrl;
wait_queue_head_t frame_wait;
struct armada_frame_work *frame_work;
spinlock_t irq_lock; spinlock_t irq_lock;
uint32_t irq_ena; uint32_t irq_ena;
struct list_head vbl_list;
}; };
#define drm_to_armada_crtc(c) container_of(c, struct armada_crtc, crtc) #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_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_gamma_get(struct drm_crtc *, u16 *, u16 *, u16 *, int);
void armada_drm_crtc_disable_irq(struct armada_crtc *, u32); void armada_drm_crtc_disable_irq(struct armada_crtc *, u32);
void armada_drm_crtc_enable_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_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; extern struct platform_driver armada_lcd_platform_driver;
#endif #endif
...@@ -37,22 +37,6 @@ static inline uint32_t armada_pitch(uint32_t width, uint32_t bpp) ...@@ -37,22 +37,6 @@ static inline uint32_t armada_pitch(uint32_t width, uint32_t bpp)
return ALIGN(pitch, 128); 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; struct armada_private;
......
...@@ -18,47 +18,6 @@ ...@@ -18,47 +18,6 @@
#include <drm/armada_drm.h> #include <drm/armada_drm.h>
#include "armada_ioctlP.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 = &params,
},
};
#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) static void armada_drm_unref_work(struct work_struct *work)
{ {
struct armada_private *priv = struct armada_private *priv =
...@@ -91,16 +50,11 @@ void armada_drm_queue_unref_work(struct drm_device *dev, ...@@ -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) 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 armada_private *priv;
struct resource *res[ARRAY_SIZE(priv->dcrtc)];
struct resource *mem = NULL; 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, struct resource *r = platform_get_resource(dev->platformdev,
IORESOURCE_MEM, n); IORESOURCE_MEM, n);
if (!r) if (!r)
...@@ -109,8 +63,6 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags) ...@@ -109,8 +63,6 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags)
/* Resources above 64K are graphics memory */ /* Resources above 64K are graphics memory */
if (resource_size(r) > SZ_64K) if (resource_size(r) > SZ_64K)
mem = r; mem = r;
else if (i < ARRAY_SIZE(priv->dcrtc))
res[i++] = r;
else else
return -EINVAL; return -EINVAL;
} }
...@@ -131,13 +83,6 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags) ...@@ -131,13 +83,6 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags)
platform_set_drvdata(dev->platformdev, dev); platform_set_drvdata(dev->platformdev, dev);
dev->dev_private = priv; 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_WORK(&priv->fb_unref_work, armada_drm_unref_work);
INIT_KFIFO(priv->fb_unref); INIT_KFIFO(priv->fb_unref);
...@@ -157,34 +102,9 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags) ...@@ -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; dev->mode_config.funcs = &armada_drm_mode_config_funcs;
drm_mm_init(&priv->linear, mem->start, resource_size(mem)); drm_mm_init(&priv->linear, mem->start, resource_size(mem));
/* Create all LCD controllers */ ret = component_bind_all(dev->dev, dev);
for (n = 0; n < ARRAY_SIZE(priv->dcrtc); n++) { if (ret)
int irq; goto err_kms;
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 = drm_vblank_init(dev, dev->mode_config.num_crtc); ret = drm_vblank_init(dev, dev->mode_config.num_crtc);
if (ret) if (ret)
...@@ -202,8 +122,7 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags) ...@@ -202,8 +122,7 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags)
return 0; return 0;
err_comp: err_comp:
if (is_componentized(dev->dev)) component_unbind_all(dev->dev, dev);
component_unbind_all(dev->dev, dev);
err_kms: err_kms:
drm_mode_config_cleanup(dev); drm_mode_config_cleanup(dev);
drm_mm_takedown(&priv->linear); drm_mm_takedown(&priv->linear);
...@@ -219,8 +138,7 @@ static int armada_drm_unload(struct drm_device *dev) ...@@ -219,8 +138,7 @@ static int armada_drm_unload(struct drm_device *dev)
drm_kms_helper_poll_fini(dev); drm_kms_helper_poll_fini(dev);
armada_fbdev_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_mode_config_cleanup(dev);
drm_mm_takedown(&priv->linear); drm_mm_takedown(&priv->linear);
...@@ -230,29 +148,6 @@ static int armada_drm_unload(struct drm_device *dev) ...@@ -230,29 +148,6 @@ static int armada_drm_unload(struct drm_device *dev)
return 0; 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. */ /* These are called under the vbl_lock. */
static int armada_drm_enable_vblank(struct drm_device *dev, int crtc) 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 = { ...@@ -435,37 +330,28 @@ static const struct component_master_ops armada_master_ops = {
static int armada_drm_probe(struct platform_device *pdev) static int armada_drm_probe(struct platform_device *pdev)
{ {
if (is_componentized(&pdev->dev)) { struct component_match *match = NULL;
struct component_match *match = NULL; int ret;
int ret;
ret = armada_drm_find_components(&pdev->dev, &match);
ret = armada_drm_find_components(&pdev->dev, &match); if (ret < 0)
if (ret < 0) return ret;
return ret;
return component_master_add_with_match(&pdev->dev, &armada_master_ops,
return component_master_add_with_match(&pdev->dev, match);
&armada_master_ops, match);
} else {
return drm_platform_init(&armada_drm_driver, pdev);
}
} }
static int armada_drm_remove(struct platform_device *pdev) static int armada_drm_remove(struct platform_device *pdev)
{ {
if (is_componentized(&pdev->dev)) component_master_del(&pdev->dev, &armada_master_ops);
component_master_del(&pdev->dev, &armada_master_ops);
else
drm_put_dev(platform_get_drvdata(pdev));
return 0; return 0;
} }
static const struct platform_device_id armada_drm_platform_ids[] = { static const struct platform_device_id armada_drm_platform_ids[] = {
{ {
.name = "armada-drm", .name = "armada-drm",
.driver_data = (unsigned long)&armada510_ops,
}, { }, {
.name = "armada-510-drm", .name = "armada-510-drm",
.driver_data = (unsigned long)&armada510_ops,
}, },
{ }, { },
}; };
......
/*
* 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;
}
/*
* 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 @@ ...@@ -16,7 +16,7 @@
#include <drm/armada_drm.h> #include <drm/armada_drm.h>
#include "armada_ioctlP.h" #include "armada_ioctlP.h"
struct armada_plane_properties { struct armada_ovl_plane_properties {
uint32_t colorkey_yr; uint32_t colorkey_yr;
uint32_t colorkey_ug; uint32_t colorkey_ug;
uint32_t colorkey_vb; uint32_t colorkey_vb;
...@@ -29,26 +29,25 @@ struct armada_plane_properties { ...@@ -29,26 +29,25 @@ struct armada_plane_properties {
uint32_t colorkey_mode; uint32_t colorkey_mode;
}; };
struct armada_plane { struct armada_ovl_plane {
struct drm_plane base; struct armada_plane base;
spinlock_t lock;
struct drm_framebuffer *old_fb; struct drm_framebuffer *old_fb;
uint32_t src_hw; uint32_t src_hw;
uint32_t dst_hw; uint32_t dst_hw;
uint32_t dst_yx; uint32_t dst_yx;
uint32_t ctrl0; uint32_t ctrl0;
struct { struct {
struct armada_vbl_event update; struct armada_plane_work work;
struct armada_regs regs[13]; struct armada_regs regs[13];
wait_queue_head_t wait;
} vbl; } 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 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) struct armada_crtc *dcrtc)
{ {
writel_relaxed(prop->colorkey_yr, dcrtc->base + LCD_SPU_COLORKEY_Y); writel_relaxed(prop->colorkey_yr, dcrtc->base + LCD_SPU_COLORKEY_Y);
...@@ -71,32 +70,34 @@ armada_ovl_update_attr(struct armada_plane_properties *prop, ...@@ -71,32 +70,34 @@ armada_ovl_update_attr(struct armada_plane_properties *prop,
spin_unlock_irq(&dcrtc->irq_lock); spin_unlock_irq(&dcrtc->irq_lock);
} }
/* === Plane support === */ static void armada_ovl_retire_fb(struct armada_ovl_plane *dplane,
static void armada_plane_vbl(struct armada_crtc *dcrtc, void *data) struct drm_framebuffer *fb)
{ {
struct armada_plane *dplane = data; struct drm_framebuffer *old_fb;
struct drm_framebuffer *fb;
armada_drm_crtc_update_regs(dcrtc, dplane->vbl.regs); old_fb = xchg(&dplane->old_fb, fb);
spin_lock(&dplane->lock); if (old_fb)
fb = dplane->old_fb; armada_drm_queue_unref_work(dplane->base.base.dev, old_fb);
dplane->old_fb = NULL; }
spin_unlock(&dplane->lock);
if (fb) /* === Plane support === */
armada_drm_queue_unref_work(dcrtc->crtc.dev, fb); 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);
wake_up(&dplane->vbl.wait); armada_drm_crtc_update_regs(dcrtc, dplane->vbl.regs);
armada_ovl_retire_fb(dplane, NULL);
} }
static int 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, struct drm_framebuffer *fb,
int crtc_x, int crtc_y, unsigned crtc_w, unsigned crtc_h, 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) 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 armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
struct drm_rect src = { struct drm_rect src = {
.x1 = src_x, .x1 = src_x,
...@@ -160,9 +161,8 @@ armada_plane_update(struct drm_plane *plane, struct drm_crtc *crtc, ...@@ -160,9 +161,8 @@ armada_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
dcrtc->base + LCD_SPU_SRAM_PARA1); dcrtc->base + LCD_SPU_SRAM_PARA1);
} }
wait_event_timeout(dplane->vbl.wait, if (armada_drm_plane_work_wait(&dplane->base, HZ / 25) == 0)
list_empty(&dplane->vbl.update.node), armada_drm_plane_work_cancel(dcrtc, &dplane->base);
HZ/25);
if (plane->fb != fb) { if (plane->fb != fb) {
struct armada_gem_object *obj = drm_fb_obj(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, ...@@ -175,17 +175,8 @@ armada_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
*/ */
drm_framebuffer_reference(fb); drm_framebuffer_reference(fb);
if (plane->fb) { if (plane->fb)
struct drm_framebuffer *older_fb; armada_ovl_retire_fb(dplane, plane->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);
}
src_y = src.y1 >> 16; src_y = src.y1 >> 16;
src_x = src.x1 >> 16; src_x = src.x1 >> 16;
...@@ -262,60 +253,50 @@ armada_plane_update(struct drm_plane *plane, struct drm_crtc *crtc, ...@@ -262,60 +253,50 @@ armada_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
} }
if (idx) { if (idx) {
armada_reg_queue_end(dplane->vbl.regs, 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; 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 drm_framebuffer *fb;
struct armada_crtc *dcrtc; struct armada_crtc *dcrtc;
if (!dplane->base.crtc) if (!dplane->base.base.crtc)
return 0; return 0;
dcrtc = drm_to_armada_crtc(dplane->base.crtc); dcrtc = drm_to_armada_crtc(dplane->base.base.crtc);
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_drm_plane_work_cancel(dcrtc, &dplane->base);
armada_updatel(CFG_PDWN16x66 | CFG_PDWN32x66, 0, armada_drm_crtc_plane_disable(dcrtc, plane);
dcrtc->base + LCD_SPU_SRAM_PARA1);
if (plane->fb) dcrtc->plane = NULL;
drm_framebuffer_unreference(plane->fb); dplane->ctrl0 = 0;
spin_lock_irq(&dplane->lock); fb = xchg(&dplane->old_fb, NULL);
fb = dplane->old_fb;
dplane->old_fb = NULL;
spin_unlock_irq(&dplane->lock);
if (fb) if (fb)
drm_framebuffer_unreference(fb); drm_framebuffer_unreference(fb);
return 0; 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); drm_plane_cleanup(plane);
kfree(dplane); 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 drm_property *property, uint64_t val)
{ {
struct armada_private *priv = plane->dev->dev_private; 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; bool update_attr = false;
if (property == priv->colorkey_prop) { if (property == priv->colorkey_prop) {
...@@ -372,21 +353,21 @@ static int armada_plane_set_property(struct drm_plane *plane, ...@@ -372,21 +353,21 @@ static int armada_plane_set_property(struct drm_plane *plane,
update_attr = true; update_attr = true;
} }
if (update_attr && dplane->base.crtc) if (update_attr && dplane->base.base.crtc)
armada_ovl_update_attr(&dplane->prop, armada_ovl_update_attr(&dplane->prop,
drm_to_armada_crtc(dplane->base.crtc)); drm_to_armada_crtc(dplane->base.base.crtc));
return 0; return 0;
} }
static const struct drm_plane_funcs armada_plane_funcs = { static const struct drm_plane_funcs armada_ovl_plane_funcs = {
.update_plane = armada_plane_update, .update_plane = armada_ovl_plane_update,
.disable_plane = armada_plane_disable, .disable_plane = armada_ovl_plane_disable,
.destroy = armada_plane_destroy, .destroy = armada_ovl_plane_destroy,
.set_property = armada_plane_set_property, .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_UYVY,
DRM_FORMAT_YUYV, DRM_FORMAT_YUYV,
DRM_FORMAT_YUV420, DRM_FORMAT_YUV420,
...@@ -456,7 +437,7 @@ int armada_overlay_plane_create(struct drm_device *dev, unsigned long crtcs) ...@@ -456,7 +437,7 @@ int armada_overlay_plane_create(struct drm_device *dev, unsigned long crtcs)
{ {
struct armada_private *priv = dev->dev_private; struct armada_private *priv = dev->dev_private;
struct drm_mode_object *mobj; struct drm_mode_object *mobj;
struct armada_plane *dplane; struct armada_ovl_plane *dplane;
int ret; int ret;
ret = armada_overlay_create_properties(dev); ret = armada_overlay_create_properties(dev);
...@@ -467,13 +448,23 @@ int armada_overlay_plane_create(struct drm_device *dev, unsigned long crtcs) ...@@ -467,13 +448,23 @@ int armada_overlay_plane_create(struct drm_device *dev, unsigned long crtcs)
if (!dplane) if (!dplane)
return -ENOMEM; return -ENOMEM;
spin_lock_init(&dplane->lock); ret = armada_drm_plane_init(&dplane->base);
init_waitqueue_head(&dplane->vbl.wait); if (ret) {
armada_drm_vbl_event_init(&dplane->vbl.update, armada_plane_vbl, kfree(dplane);
dplane); return ret;
}
dplane->vbl.work.fn = armada_ovl_plane_work;
drm_plane_init(dev, &dplane->base, crtcs, &armada_plane_funcs, ret = drm_universal_plane_init(dev, &dplane->base.base, crtcs,
armada_formats, ARRAY_SIZE(armada_formats), false); &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_yr = 0xfefefe00;
dplane->prop.colorkey_ug = 0x01010100; dplane->prop.colorkey_ug = 0x01010100;
...@@ -483,7 +474,7 @@ int armada_overlay_plane_create(struct drm_device *dev, unsigned long crtcs) ...@@ -483,7 +474,7 @@ int armada_overlay_plane_create(struct drm_device *dev, unsigned long crtcs)
dplane->prop.contrast = 0x4000; dplane->prop.contrast = 0x4000;
dplane->prop.saturation = 0x4000; dplane->prop.saturation = 0x4000;
mobj = &dplane->base.base; mobj = &dplane->base.base.base;
drm_object_attach_property(mobj, priv->colorkey_prop, drm_object_attach_property(mobj, priv->colorkey_prop,
0x0101fe); 0x0101fe);
drm_object_attach_property(mobj, priv->colorkey_min_prop, drm_object_attach_property(mobj, priv->colorkey_min_prop,
......
/*
* 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);
}
/*
* 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
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment