Commit 7e477484 authored by Dave Airlie's avatar Dave Airlie

Merge branch 'drm/next/atomic' of git://linuxtv.org/pinchartl/fbdev into drm-next

rcar-du atomic modesetting support
* 'drm/next/atomic' of git://linuxtv.org/pinchartl/fbdev: (32 commits)
  drm: rcar-du: Fix race condition in hardware plane allocator
  drm: rcar-du: Move group locking inside rcar_du_crtc_update_planes()
  drm: rcar-du: Move plane commit code from CRTC start to CRTC resume
  drm: rcar-du: Move plane format to plane state
  drm: rcar-du: Remove unneeded rcar_du_crtc plane field
  drm: rcar-du: Replace plane crtc and enabled fields by plane state
  drm: rcar-du: Rework plane setup code
  drm: rcar-du: Switch plane set_property to atomic helpers
  drm: rcar-du: Switch page flip to atomic helpers
  drm: rcar-du: Implement asynchronous commit support
  drm: rcar-du: Replace encoder mode_fixup with atomic_check
  drm: rcar-du: Switch connector DPMS to atomic helpers
  drm: rcar-du: Switch mode config to atomic helpers
  drm: rcar-du: Switch plane update to atomic helpers
  drm: rcar-du: Rework CRTC enable/disable for atomic updates
  drm: rcar-du: Rework HDMI encoder enable/disable for atomic updates
  drm: rcar-du: Rework encoder enable/disable for atomic updates
  drm: rcar-du: Replace LVDS encoder DPMS by enable/disable
  drm: rcar-du: Remove private copy of plane size and position
  drm: rcar-du: Wire up atomic state object scaffolding
  ...
parents 79d6d942 5ee5a81d
...@@ -15,6 +15,8 @@ ...@@ -15,6 +15,8 @@
#include <linux/mutex.h> #include <linux/mutex.h>
#include <drm/drmP.h> #include <drm/drmP.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc.h> #include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h> #include <drm/drm_crtc_helper.h>
#include <drm/drm_fb_cma_helper.h> #include <drm/drm_fb_cma_helper.h>
...@@ -99,9 +101,13 @@ static void rcar_du_crtc_put(struct rcar_du_crtc *rcrtc) ...@@ -99,9 +101,13 @@ static void rcar_du_crtc_put(struct rcar_du_crtc *rcrtc)
clk_disable_unprepare(rcrtc->clock); clk_disable_unprepare(rcrtc->clock);
} }
/* -----------------------------------------------------------------------------
* Hardware Setup
*/
static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc) static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc)
{ {
const struct drm_display_mode *mode = &rcrtc->crtc.mode; const struct drm_display_mode *mode = &rcrtc->crtc.state->adjusted_mode;
unsigned long mode_clock = mode->clock * 1000; unsigned long mode_clock = mode->clock * 1000;
unsigned long clk; unsigned long clk;
u32 value; u32 value;
...@@ -187,9 +193,19 @@ void rcar_du_crtc_route_output(struct drm_crtc *crtc, ...@@ -187,9 +193,19 @@ void rcar_du_crtc_route_output(struct drm_crtc *crtc,
rcdu->dpad0_source = rcrtc->index; rcdu->dpad0_source = rcrtc->index;
} }
void rcar_du_crtc_update_planes(struct drm_crtc *crtc) static unsigned int plane_zpos(struct rcar_du_plane *plane)
{
return to_rcar_du_plane_state(plane->plane.state)->zpos;
}
static const struct rcar_du_format_info *
plane_format(struct rcar_du_plane *plane)
{
return to_rcar_du_plane_state(plane->plane.state)->format;
}
static void rcar_du_crtc_update_planes(struct rcar_du_crtc *rcrtc)
{ {
struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
struct rcar_du_plane *planes[RCAR_DU_NUM_HW_PLANES]; struct rcar_du_plane *planes[RCAR_DU_NUM_HW_PLANES];
unsigned int num_planes = 0; unsigned int num_planes = 0;
unsigned int prio = 0; unsigned int prio = 0;
...@@ -201,29 +217,30 @@ void rcar_du_crtc_update_planes(struct drm_crtc *crtc) ...@@ -201,29 +217,30 @@ void rcar_du_crtc_update_planes(struct drm_crtc *crtc)
struct rcar_du_plane *plane = &rcrtc->group->planes.planes[i]; struct rcar_du_plane *plane = &rcrtc->group->planes.planes[i];
unsigned int j; unsigned int j;
if (plane->crtc != &rcrtc->crtc || !plane->enabled) if (plane->plane.state->crtc != &rcrtc->crtc)
continue; continue;
/* Insert the plane in the sorted planes array. */ /* Insert the plane in the sorted planes array. */
for (j = num_planes++; j > 0; --j) { for (j = num_planes++; j > 0; --j) {
if (planes[j-1]->zpos <= plane->zpos) if (plane_zpos(planes[j-1]) <= plane_zpos(plane))
break; break;
planes[j] = planes[j-1]; planes[j] = planes[j-1];
} }
planes[j] = plane; planes[j] = plane;
prio += plane->format->planes * 4; prio += plane_format(plane)->planes * 4;
} }
for (i = 0; i < num_planes; ++i) { for (i = 0; i < num_planes; ++i) {
struct rcar_du_plane *plane = planes[i]; struct rcar_du_plane *plane = planes[i];
unsigned int index = plane->hwindex; struct drm_plane_state *state = plane->plane.state;
unsigned int index = to_rcar_du_plane_state(state)->hwindex;
prio -= 4; prio -= 4;
dspr |= (index + 1) << prio; dspr |= (index + 1) << prio;
dptsr |= DPTSR_PnDK(index) | DPTSR_PnTS(index); dptsr |= DPTSR_PnDK(index) | DPTSR_PnTS(index);
if (plane->format->planes == 2) { if (plane_format(plane)->planes == 2) {
index = (index + 1) % 8; index = (index + 1) % 8;
prio -= 4; prio -= 4;
...@@ -236,8 +253,6 @@ void rcar_du_crtc_update_planes(struct drm_crtc *crtc) ...@@ -236,8 +253,6 @@ void rcar_du_crtc_update_planes(struct drm_crtc *crtc)
* with superposition controller 2. * with superposition controller 2.
*/ */
if (rcrtc->index % 2) { if (rcrtc->index % 2) {
u32 value = rcar_du_group_read(rcrtc->group, DPTSR);
/* The DPTSR register is updated when the display controller is /* The DPTSR register is updated when the display controller is
* stopped. We thus need to restart the DU. Once again, sorry * stopped. We thus need to restart the DU. Once again, sorry
* for the flicker. One way to mitigate the issue would be to * for the flicker. One way to mitigate the issue would be to
...@@ -245,29 +260,104 @@ void rcar_du_crtc_update_planes(struct drm_crtc *crtc) ...@@ -245,29 +260,104 @@ void rcar_du_crtc_update_planes(struct drm_crtc *crtc)
* split, or through a module parameter). Flicker would then * split, or through a module parameter). Flicker would then
* occur only if we need to break the pre-association. * occur only if we need to break the pre-association.
*/ */
if (value != dptsr) { mutex_lock(&rcrtc->group->lock);
if (rcar_du_group_read(rcrtc->group, DPTSR) != dptsr) {
rcar_du_group_write(rcrtc->group, DPTSR, dptsr); rcar_du_group_write(rcrtc->group, DPTSR, dptsr);
if (rcrtc->group->used_crtcs) if (rcrtc->group->used_crtcs)
rcar_du_group_restart(rcrtc->group); rcar_du_group_restart(rcrtc->group);
} }
mutex_unlock(&rcrtc->group->lock);
} }
rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? DS2PR : DS1PR, rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? DS2PR : DS1PR,
dspr); dspr);
} }
/* -----------------------------------------------------------------------------
* Page Flip
*/
void rcar_du_crtc_cancel_page_flip(struct rcar_du_crtc *rcrtc,
struct drm_file *file)
{
struct drm_pending_vblank_event *event;
struct drm_device *dev = rcrtc->crtc.dev;
unsigned long flags;
/* Destroy the pending vertical blanking event associated with the
* pending page flip, if any, and disable vertical blanking interrupts.
*/
spin_lock_irqsave(&dev->event_lock, flags);
event = rcrtc->event;
if (event && event->base.file_priv == file) {
rcrtc->event = NULL;
event->base.destroy(&event->base);
drm_crtc_vblank_put(&rcrtc->crtc);
}
spin_unlock_irqrestore(&dev->event_lock, flags);
}
static void rcar_du_crtc_finish_page_flip(struct rcar_du_crtc *rcrtc)
{
struct drm_pending_vblank_event *event;
struct drm_device *dev = rcrtc->crtc.dev;
unsigned long flags;
spin_lock_irqsave(&dev->event_lock, flags);
event = rcrtc->event;
rcrtc->event = NULL;
spin_unlock_irqrestore(&dev->event_lock, flags);
if (event == NULL)
return;
spin_lock_irqsave(&dev->event_lock, flags);
drm_send_vblank_event(dev, rcrtc->index, event);
wake_up(&rcrtc->flip_wait);
spin_unlock_irqrestore(&dev->event_lock, flags);
drm_crtc_vblank_put(&rcrtc->crtc);
}
static bool rcar_du_crtc_page_flip_pending(struct rcar_du_crtc *rcrtc)
{
struct drm_device *dev = rcrtc->crtc.dev;
unsigned long flags;
bool pending;
spin_lock_irqsave(&dev->event_lock, flags);
pending = rcrtc->event != NULL;
spin_unlock_irqrestore(&dev->event_lock, flags);
return pending;
}
static void rcar_du_crtc_wait_page_flip(struct rcar_du_crtc *rcrtc)
{
struct rcar_du_device *rcdu = rcrtc->group->dev;
if (wait_event_timeout(rcrtc->flip_wait,
!rcar_du_crtc_page_flip_pending(rcrtc),
msecs_to_jiffies(50)))
return;
dev_warn(rcdu->dev, "page flip timeout\n");
rcar_du_crtc_finish_page_flip(rcrtc);
}
/* -----------------------------------------------------------------------------
* Start/Stop and Suspend/Resume
*/
static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc) static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc)
{ {
struct drm_crtc *crtc = &rcrtc->crtc; struct drm_crtc *crtc = &rcrtc->crtc;
bool interlaced; bool interlaced;
unsigned int i;
if (rcrtc->started) if (rcrtc->started)
return; return;
if (WARN_ON(rcrtc->plane->format == NULL))
return;
/* Set display off and background to black */ /* Set display off and background to black */
rcar_du_crtc_write(rcrtc, DOOR, DOOR_RGB(0, 0, 0)); rcar_du_crtc_write(rcrtc, DOOR, DOOR_RGB(0, 0, 0));
rcar_du_crtc_write(rcrtc, BPOR, BPOR_RGB(0, 0, 0)); rcar_du_crtc_write(rcrtc, BPOR, BPOR_RGB(0, 0, 0));
...@@ -276,20 +366,8 @@ static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc) ...@@ -276,20 +366,8 @@ static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc)
rcar_du_crtc_set_display_timing(rcrtc); rcar_du_crtc_set_display_timing(rcrtc);
rcar_du_group_set_routing(rcrtc->group); rcar_du_group_set_routing(rcrtc->group);
mutex_lock(&rcrtc->group->planes.lock); /* Start with all planes disabled. */
rcrtc->plane->enabled = true; rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? DS2PR : DS1PR, 0);
rcar_du_crtc_update_planes(crtc);
mutex_unlock(&rcrtc->group->planes.lock);
/* Setup planes. */
for (i = 0; i < ARRAY_SIZE(rcrtc->group->planes.planes); ++i) {
struct rcar_du_plane *plane = &rcrtc->group->planes.planes[i];
if (plane->crtc != crtc || !plane->enabled)
continue;
rcar_du_plane_setup(plane);
}
/* Select master sync mode. This enables display operation in master /* Select master sync mode. This enables display operation in master
* sync mode (with the HSYNC and VSYNC signals configured as outputs and * sync mode (with the HSYNC and VSYNC signals configured as outputs and
...@@ -302,6 +380,9 @@ static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc) ...@@ -302,6 +380,9 @@ static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc)
rcar_du_group_start_stop(rcrtc->group, true); rcar_du_group_start_stop(rcrtc->group, true);
/* Turn vertical blanking interrupt reporting back on. */
drm_crtc_vblank_on(crtc);
rcrtc->started = true; rcrtc->started = true;
} }
...@@ -312,10 +393,12 @@ static void rcar_du_crtc_stop(struct rcar_du_crtc *rcrtc) ...@@ -312,10 +393,12 @@ static void rcar_du_crtc_stop(struct rcar_du_crtc *rcrtc)
if (!rcrtc->started) if (!rcrtc->started)
return; return;
mutex_lock(&rcrtc->group->planes.lock); /* Disable vertical blanking interrupt reporting. We first need to wait
rcrtc->plane->enabled = false; * for page flip completion before stopping the CRTC as userspace
rcar_du_crtc_update_planes(crtc); * expects page flips to eventually complete.
mutex_unlock(&rcrtc->group->planes.lock); */
rcar_du_crtc_wait_page_flip(rcrtc);
drm_crtc_vblank_off(crtc);
/* Select switch sync mode. This stops display operation and configures /* Select switch sync mode. This stops display operation and configures
* the HSYNC and VSYNC signals as inputs. * the HSYNC and VSYNC signals as inputs.
...@@ -335,196 +418,111 @@ void rcar_du_crtc_suspend(struct rcar_du_crtc *rcrtc) ...@@ -335,196 +418,111 @@ void rcar_du_crtc_suspend(struct rcar_du_crtc *rcrtc)
void rcar_du_crtc_resume(struct rcar_du_crtc *rcrtc) void rcar_du_crtc_resume(struct rcar_du_crtc *rcrtc)
{ {
if (rcrtc->dpms != DRM_MODE_DPMS_ON) unsigned int i;
if (!rcrtc->enabled)
return; return;
rcar_du_crtc_get(rcrtc); rcar_du_crtc_get(rcrtc);
rcar_du_crtc_start(rcrtc); rcar_du_crtc_start(rcrtc);
}
static void rcar_du_crtc_update_base(struct rcar_du_crtc *rcrtc)
{
struct drm_crtc *crtc = &rcrtc->crtc;
rcar_du_plane_compute_base(rcrtc->plane, crtc->primary->fb);
rcar_du_plane_update_base(rcrtc->plane);
}
static void rcar_du_crtc_dpms(struct drm_crtc *crtc, int mode)
{
struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
if (mode != DRM_MODE_DPMS_ON) /* Commit the planes state. */
mode = DRM_MODE_DPMS_OFF; for (i = 0; i < ARRAY_SIZE(rcrtc->group->planes.planes); ++i) {
struct rcar_du_plane *plane = &rcrtc->group->planes.planes[i];
if (rcrtc->dpms == mode) if (plane->plane.state->crtc != &rcrtc->crtc)
return; continue;
if (mode == DRM_MODE_DPMS_ON) { rcar_du_plane_setup(plane);
rcar_du_crtc_get(rcrtc);
rcar_du_crtc_start(rcrtc);
} else {
rcar_du_crtc_stop(rcrtc);
rcar_du_crtc_put(rcrtc);
} }
rcrtc->dpms = mode; rcar_du_crtc_update_planes(rcrtc);
} }
static bool rcar_du_crtc_mode_fixup(struct drm_crtc *crtc, /* -----------------------------------------------------------------------------
const struct drm_display_mode *mode, * CRTC Functions
struct drm_display_mode *adjusted_mode) */
{
/* TODO Fixup modes */
return true;
}
static void rcar_du_crtc_mode_prepare(struct drm_crtc *crtc) static void rcar_du_crtc_enable(struct drm_crtc *crtc)
{ {
struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
/* We need to access the hardware during mode set, acquire a reference if (rcrtc->enabled)
* to the CRTC. return;
*/
rcar_du_crtc_get(rcrtc);
/* Stop the CRTC and release the plane. Force the DPMS mode to off as a rcar_du_crtc_get(rcrtc);
* result. rcar_du_crtc_start(rcrtc);
*/
rcar_du_crtc_stop(rcrtc);
rcar_du_plane_release(rcrtc->plane);
rcrtc->dpms = DRM_MODE_DPMS_OFF; rcrtc->enabled = true;
} }
static int rcar_du_crtc_mode_set(struct drm_crtc *crtc, static void rcar_du_crtc_disable(struct drm_crtc *crtc)
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode,
int x, int y,
struct drm_framebuffer *old_fb)
{ {
struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
struct rcar_du_device *rcdu = rcrtc->group->dev;
const struct rcar_du_format_info *format;
int ret;
format = rcar_du_format_info(crtc->primary->fb->pixel_format);
if (format == NULL) {
dev_dbg(rcdu->dev, "mode_set: unsupported format %08x\n",
crtc->primary->fb->pixel_format);
ret = -EINVAL;
goto error;
}
ret = rcar_du_plane_reserve(rcrtc->plane, format); if (!rcrtc->enabled)
if (ret < 0) return;
goto error;
rcrtc->plane->format = format;
rcrtc->plane->src_x = x;
rcrtc->plane->src_y = y;
rcrtc->plane->width = mode->hdisplay;
rcrtc->plane->height = mode->vdisplay;
rcar_du_plane_compute_base(rcrtc->plane, crtc->primary->fb); rcar_du_crtc_stop(rcrtc);
rcar_du_crtc_put(rcrtc);
rcrtc->enabled = false;
rcrtc->outputs = 0; rcrtc->outputs = 0;
return 0;
error:
/* There's no rollback/abort operation to clean up in case of error. We
* thus need to release the reference to the CRTC acquired in prepare()
* here.
*/
rcar_du_crtc_put(rcrtc);
return ret;
} }
static void rcar_du_crtc_mode_commit(struct drm_crtc *crtc) static bool rcar_du_crtc_mode_fixup(struct drm_crtc *crtc,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{ {
struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); /* TODO Fixup modes */
return true;
/* We're done, restart the CRTC and set the DPMS mode to on. The
* reference to the DU acquired at prepare() time will thus be released
* by the DPMS handler (possibly called by the disable() handler).
*/
rcar_du_crtc_start(rcrtc);
rcrtc->dpms = DRM_MODE_DPMS_ON;
} }
static int rcar_du_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, static void rcar_du_crtc_atomic_begin(struct drm_crtc *crtc)
struct drm_framebuffer *old_fb)
{ {
struct drm_pending_vblank_event *event = crtc->state->event;
struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
struct drm_device *dev = rcrtc->crtc.dev;
unsigned long flags;
rcrtc->plane->src_x = x; if (event) {
rcrtc->plane->src_y = y; event->pipe = rcrtc->index;
rcar_du_crtc_update_base(rcrtc); WARN_ON(drm_crtc_vblank_get(crtc) != 0);
return 0; spin_lock_irqsave(&dev->event_lock, flags);
rcrtc->event = event;
spin_unlock_irqrestore(&dev->event_lock, flags);
}
} }
static void rcar_du_crtc_disable(struct drm_crtc *crtc) static void rcar_du_crtc_atomic_flush(struct drm_crtc *crtc)
{ {
struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
rcar_du_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); rcar_du_crtc_update_planes(rcrtc);
rcar_du_plane_release(rcrtc->plane);
} }
static const struct drm_crtc_helper_funcs crtc_helper_funcs = { static const struct drm_crtc_helper_funcs crtc_helper_funcs = {
.dpms = rcar_du_crtc_dpms,
.mode_fixup = rcar_du_crtc_mode_fixup, .mode_fixup = rcar_du_crtc_mode_fixup,
.prepare = rcar_du_crtc_mode_prepare,
.commit = rcar_du_crtc_mode_commit,
.mode_set = rcar_du_crtc_mode_set,
.mode_set_base = rcar_du_crtc_mode_set_base,
.disable = rcar_du_crtc_disable, .disable = rcar_du_crtc_disable,
.enable = rcar_du_crtc_enable,
.atomic_begin = rcar_du_crtc_atomic_begin,
.atomic_flush = rcar_du_crtc_atomic_flush,
}; };
void rcar_du_crtc_cancel_page_flip(struct rcar_du_crtc *rcrtc, static const struct drm_crtc_funcs crtc_funcs = {
struct drm_file *file) .reset = drm_atomic_helper_crtc_reset,
{ .destroy = drm_crtc_cleanup,
struct drm_pending_vblank_event *event; .set_config = drm_atomic_helper_set_config,
struct drm_device *dev = rcrtc->crtc.dev; .page_flip = drm_atomic_helper_page_flip,
unsigned long flags; .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
/* Destroy the pending vertical blanking event associated with the };
* pending page flip, if any, and disable vertical blanking interrupts.
*/
spin_lock_irqsave(&dev->event_lock, flags);
event = rcrtc->event;
if (event && event->base.file_priv == file) {
rcrtc->event = NULL;
event->base.destroy(&event->base);
drm_vblank_put(dev, rcrtc->index);
}
spin_unlock_irqrestore(&dev->event_lock, flags);
}
static void rcar_du_crtc_finish_page_flip(struct rcar_du_crtc *rcrtc)
{
struct drm_pending_vblank_event *event;
struct drm_device *dev = rcrtc->crtc.dev;
unsigned long flags;
spin_lock_irqsave(&dev->event_lock, flags);
event = rcrtc->event;
rcrtc->event = NULL;
spin_unlock_irqrestore(&dev->event_lock, flags);
if (event == NULL)
return;
spin_lock_irqsave(&dev->event_lock, flags);
drm_send_vblank_event(dev, rcrtc->index, event);
spin_unlock_irqrestore(&dev->event_lock, flags);
drm_vblank_put(dev, rcrtc->index); /* -----------------------------------------------------------------------------
} * Interrupt Handling
*/
static irqreturn_t rcar_du_crtc_irq(int irq, void *arg) static irqreturn_t rcar_du_crtc_irq(int irq, void *arg)
{ {
...@@ -544,41 +542,9 @@ static irqreturn_t rcar_du_crtc_irq(int irq, void *arg) ...@@ -544,41 +542,9 @@ static irqreturn_t rcar_du_crtc_irq(int irq, void *arg)
return ret; return ret;
} }
static int rcar_du_crtc_page_flip(struct drm_crtc *crtc, /* -----------------------------------------------------------------------------
struct drm_framebuffer *fb, * Initialization
struct drm_pending_vblank_event *event, */
uint32_t page_flip_flags)
{
struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
struct drm_device *dev = rcrtc->crtc.dev;
unsigned long flags;
spin_lock_irqsave(&dev->event_lock, flags);
if (rcrtc->event != NULL) {
spin_unlock_irqrestore(&dev->event_lock, flags);
return -EBUSY;
}
spin_unlock_irqrestore(&dev->event_lock, flags);
crtc->primary->fb = fb;
rcar_du_crtc_update_base(rcrtc);
if (event) {
event->pipe = rcrtc->index;
drm_vblank_get(dev, rcrtc->index);
spin_lock_irqsave(&dev->event_lock, flags);
rcrtc->event = event;
spin_unlock_irqrestore(&dev->event_lock, flags);
}
return 0;
}
static const struct drm_crtc_funcs crtc_funcs = {
.destroy = drm_crtc_cleanup,
.set_config = drm_crtc_helper_set_config,
.page_flip = rcar_du_crtc_page_flip,
};
int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int index) int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int index)
{ {
...@@ -620,20 +586,24 @@ int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int index) ...@@ -620,20 +586,24 @@ int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int index)
return -EPROBE_DEFER; return -EPROBE_DEFER;
} }
init_waitqueue_head(&rcrtc->flip_wait);
rcrtc->group = rgrp; rcrtc->group = rgrp;
rcrtc->mmio_offset = mmio_offsets[index]; rcrtc->mmio_offset = mmio_offsets[index];
rcrtc->index = index; rcrtc->index = index;
rcrtc->dpms = DRM_MODE_DPMS_OFF; rcrtc->enabled = false;
rcrtc->plane = &rgrp->planes.planes[index % 2];
rcrtc->plane->crtc = crtc;
ret = drm_crtc_init(rcdu->ddev, crtc, &crtc_funcs); ret = drm_crtc_init_with_planes(rcdu->ddev, crtc,
&rgrp->planes.planes[index % 2].plane,
NULL, &crtc_funcs);
if (ret < 0) if (ret < 0)
return ret; return ret;
drm_crtc_helper_add(crtc, &crtc_helper_funcs); drm_crtc_helper_add(crtc, &crtc_helper_funcs);
/* Start with vertical blanking interrupt reporting disabled. */
drm_crtc_vblank_off(crtc);
/* Register the interrupt handler. */ /* Register the interrupt handler. */
if (rcar_du_has(rcdu, RCAR_DU_FEATURE_CRTC_IRQ_CLOCK)) { if (rcar_du_has(rcdu, RCAR_DU_FEATURE_CRTC_IRQ_CLOCK)) {
irq = platform_get_irq(pdev, index); irq = platform_get_irq(pdev, index);
......
...@@ -15,12 +15,12 @@ ...@@ -15,12 +15,12 @@
#define __RCAR_DU_CRTC_H__ #define __RCAR_DU_CRTC_H__
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/wait.h>
#include <drm/drmP.h> #include <drm/drmP.h>
#include <drm/drm_crtc.h> #include <drm/drm_crtc.h>
struct rcar_du_group; struct rcar_du_group;
struct rcar_du_plane;
struct rcar_du_crtc { struct rcar_du_crtc {
struct drm_crtc crtc; struct drm_crtc crtc;
...@@ -32,11 +32,12 @@ struct rcar_du_crtc { ...@@ -32,11 +32,12 @@ struct rcar_du_crtc {
bool started; bool started;
struct drm_pending_vblank_event *event; struct drm_pending_vblank_event *event;
wait_queue_head_t flip_wait;
unsigned int outputs; unsigned int outputs;
int dpms; bool enabled;
struct rcar_du_group *group; struct rcar_du_group *group;
struct rcar_du_plane *plane;
}; };
#define to_rcar_crtc(c) container_of(c, struct rcar_du_crtc, crtc) #define to_rcar_crtc(c) container_of(c, struct rcar_du_crtc, crtc)
...@@ -59,6 +60,5 @@ void rcar_du_crtc_resume(struct rcar_du_crtc *rcrtc); ...@@ -59,6 +60,5 @@ void rcar_du_crtc_resume(struct rcar_du_crtc *rcrtc);
void rcar_du_crtc_route_output(struct drm_crtc *crtc, void rcar_du_crtc_route_output(struct drm_crtc *crtc,
enum rcar_du_output output); enum rcar_du_output output);
void rcar_du_crtc_update_planes(struct drm_crtc *crtc);
#endif /* __RCAR_DU_CRTC_H__ */ #endif /* __RCAR_DU_CRTC_H__ */
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pm.h> #include <linux/pm.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/wait.h>
#include <drm/drmP.h> #include <drm/drmP.h>
#include <drm/drm_crtc_helper.h> #include <drm/drm_crtc_helper.h>
...@@ -163,6 +164,8 @@ static int rcar_du_load(struct drm_device *dev, unsigned long flags) ...@@ -163,6 +164,8 @@ static int rcar_du_load(struct drm_device *dev, unsigned long flags)
return -ENOMEM; return -ENOMEM;
} }
init_waitqueue_head(&rcdu->commit.wait);
rcdu->dev = &pdev->dev; rcdu->dev = &pdev->dev;
rcdu->info = np ? of_match_device(rcar_du_of_table, rcdu->dev)->data rcdu->info = np ? of_match_device(rcar_du_of_table, rcdu->dev)->data
: (void *)platform_get_device_id(pdev)->driver_data; : (void *)platform_get_device_id(pdev)->driver_data;
...@@ -175,17 +178,19 @@ static int rcar_du_load(struct drm_device *dev, unsigned long flags) ...@@ -175,17 +178,19 @@ static int rcar_du_load(struct drm_device *dev, unsigned long flags)
if (IS_ERR(rcdu->mmio)) if (IS_ERR(rcdu->mmio))
return PTR_ERR(rcdu->mmio); return PTR_ERR(rcdu->mmio);
/* DRM/KMS objects */ /* Initialize vertical blanking interrupts handling. Start with vblank
ret = rcar_du_modeset_init(rcdu); * disabled for all CRTCs.
*/
ret = drm_vblank_init(dev, (1 << rcdu->info->num_crtcs) - 1);
if (ret < 0) { if (ret < 0) {
dev_err(&pdev->dev, "failed to initialize DRM/KMS\n"); dev_err(&pdev->dev, "failed to initialize vblank\n");
goto done; goto done;
} }
/* vblank handling */ /* DRM/KMS objects */
ret = drm_vblank_init(dev, (1 << rcdu->num_crtcs) - 1); ret = rcar_du_modeset_init(rcdu);
if (ret < 0) { if (ret < 0) {
dev_err(&pdev->dev, "failed to initialize vblank\n"); dev_err(&pdev->dev, "failed to initialize DRM/KMS\n");
goto done; goto done;
} }
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#define __RCAR_DU_DRV_H__ #define __RCAR_DU_DRV_H__
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/wait.h>
#include "rcar_du_crtc.h" #include "rcar_du_crtc.h"
#include "rcar_du_group.h" #include "rcar_du_group.h"
...@@ -64,6 +65,10 @@ struct rcar_du_device_info { ...@@ -64,6 +65,10 @@ struct rcar_du_device_info {
unsigned int num_lvds; unsigned int num_lvds;
}; };
#define RCAR_DU_MAX_CRTCS 3
#define RCAR_DU_MAX_GROUPS DIV_ROUND_UP(RCAR_DU_MAX_CRTCS, 2)
#define RCAR_DU_MAX_LVDS 2
struct rcar_du_device { struct rcar_du_device {
struct device *dev; struct device *dev;
const struct rcar_du_device_info *info; const struct rcar_du_device_info *info;
...@@ -73,13 +78,18 @@ struct rcar_du_device { ...@@ -73,13 +78,18 @@ struct rcar_du_device {
struct drm_device *ddev; struct drm_device *ddev;
struct drm_fbdev_cma *fbdev; struct drm_fbdev_cma *fbdev;
struct rcar_du_crtc crtcs[3]; struct rcar_du_crtc crtcs[RCAR_DU_MAX_CRTCS];
unsigned int num_crtcs; unsigned int num_crtcs;
struct rcar_du_group groups[2]; struct rcar_du_group groups[RCAR_DU_MAX_GROUPS];
unsigned int dpad0_source; unsigned int dpad0_source;
struct rcar_du_lvdsenc *lvds[2]; struct rcar_du_lvdsenc *lvds[RCAR_DU_MAX_LVDS];
struct {
wait_queue_head_t wait;
u32 pending;
} commit;
}; };
static inline bool rcar_du_has(struct rcar_du_device *rcdu, static inline bool rcar_du_has(struct rcar_du_device *rcdu,
......
...@@ -42,46 +42,40 @@ rcar_du_connector_best_encoder(struct drm_connector *connector) ...@@ -42,46 +42,40 @@ rcar_du_connector_best_encoder(struct drm_connector *connector)
* Encoder * Encoder
*/ */
static void rcar_du_encoder_dpms(struct drm_encoder *encoder, int mode) static void rcar_du_encoder_disable(struct drm_encoder *encoder)
{ {
struct rcar_du_encoder *renc = to_rcar_encoder(encoder); struct rcar_du_encoder *renc = to_rcar_encoder(encoder);
if (mode != DRM_MODE_DPMS_ON) if (renc->lvds)
mode = DRM_MODE_DPMS_OFF; rcar_du_lvdsenc_enable(renc->lvds, encoder->crtc, false);
}
static void rcar_du_encoder_enable(struct drm_encoder *encoder)
{
struct rcar_du_encoder *renc = to_rcar_encoder(encoder);
if (renc->lvds) if (renc->lvds)
rcar_du_lvdsenc_dpms(renc->lvds, encoder->crtc, mode); rcar_du_lvdsenc_enable(renc->lvds, encoder->crtc, true);
} }
static bool rcar_du_encoder_mode_fixup(struct drm_encoder *encoder, static int rcar_du_encoder_atomic_check(struct drm_encoder *encoder,
const struct drm_display_mode *mode, struct drm_crtc_state *crtc_state,
struct drm_display_mode *adjusted_mode) struct drm_connector_state *conn_state)
{ {
struct rcar_du_encoder *renc = to_rcar_encoder(encoder); struct rcar_du_encoder *renc = to_rcar_encoder(encoder);
struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode;
const struct drm_display_mode *mode = &crtc_state->mode;
const struct drm_display_mode *panel_mode; const struct drm_display_mode *panel_mode;
struct drm_connector *connector = conn_state->connector;
struct drm_device *dev = encoder->dev; struct drm_device *dev = encoder->dev;
struct drm_connector *connector;
bool found = false;
/* DAC encoders have currently no restriction on the mode. */ /* DAC encoders have currently no restriction on the mode. */
if (encoder->encoder_type == DRM_MODE_ENCODER_DAC) if (encoder->encoder_type == DRM_MODE_ENCODER_DAC)
return true; return 0;
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
if (connector->encoder == encoder) {
found = true;
break;
}
}
if (!found) {
dev_dbg(dev->dev, "mode_fixup: no connector found\n");
return false;
}
if (list_empty(&connector->modes)) { if (list_empty(&connector->modes)) {
dev_dbg(dev->dev, "mode_fixup: empty modes list\n"); dev_dbg(dev->dev, "encoder: empty modes list\n");
return false; return -EINVAL;
} }
panel_mode = list_first_entry(&connector->modes, panel_mode = list_first_entry(&connector->modes,
...@@ -90,7 +84,7 @@ static bool rcar_du_encoder_mode_fixup(struct drm_encoder *encoder, ...@@ -90,7 +84,7 @@ static bool rcar_du_encoder_mode_fixup(struct drm_encoder *encoder,
/* We're not allowed to modify the resolution. */ /* We're not allowed to modify the resolution. */
if (mode->hdisplay != panel_mode->hdisplay || if (mode->hdisplay != panel_mode->hdisplay ||
mode->vdisplay != panel_mode->vdisplay) mode->vdisplay != panel_mode->vdisplay)
return false; return -EINVAL;
/* The flat panel mode is fixed, just copy it to the adjusted mode. */ /* The flat panel mode is fixed, just copy it to the adjusted mode. */
drm_mode_copy(adjusted_mode, panel_mode); drm_mode_copy(adjusted_mode, panel_mode);
...@@ -102,25 +96,7 @@ static bool rcar_du_encoder_mode_fixup(struct drm_encoder *encoder, ...@@ -102,25 +96,7 @@ static bool rcar_du_encoder_mode_fixup(struct drm_encoder *encoder,
adjusted_mode->clock = clamp(adjusted_mode->clock, adjusted_mode->clock = clamp(adjusted_mode->clock,
30000, 150000); 30000, 150000);
return true; return 0;
}
static void rcar_du_encoder_mode_prepare(struct drm_encoder *encoder)
{
struct rcar_du_encoder *renc = to_rcar_encoder(encoder);
if (renc->lvds)
rcar_du_lvdsenc_dpms(renc->lvds, encoder->crtc,
DRM_MODE_DPMS_OFF);
}
static void rcar_du_encoder_mode_commit(struct drm_encoder *encoder)
{
struct rcar_du_encoder *renc = to_rcar_encoder(encoder);
if (renc->lvds)
rcar_du_lvdsenc_dpms(renc->lvds, encoder->crtc,
DRM_MODE_DPMS_ON);
} }
static void rcar_du_encoder_mode_set(struct drm_encoder *encoder, static void rcar_du_encoder_mode_set(struct drm_encoder *encoder,
...@@ -133,11 +109,10 @@ static void rcar_du_encoder_mode_set(struct drm_encoder *encoder, ...@@ -133,11 +109,10 @@ static void rcar_du_encoder_mode_set(struct drm_encoder *encoder,
} }
static const struct drm_encoder_helper_funcs encoder_helper_funcs = { static const struct drm_encoder_helper_funcs encoder_helper_funcs = {
.dpms = rcar_du_encoder_dpms,
.mode_fixup = rcar_du_encoder_mode_fixup,
.prepare = rcar_du_encoder_mode_prepare,
.commit = rcar_du_encoder_mode_commit,
.mode_set = rcar_du_encoder_mode_set, .mode_set = rcar_du_encoder_mode_set,
.disable = rcar_du_encoder_disable,
.enable = rcar_du_encoder_enable,
.atomic_check = rcar_du_encoder_atomic_check,
}; };
static const struct drm_encoder_funcs encoder_funcs = { static const struct drm_encoder_funcs encoder_funcs = {
......
...@@ -14,6 +14,8 @@ ...@@ -14,6 +14,8 @@
#ifndef __RCAR_DU_GROUP_H__ #ifndef __RCAR_DU_GROUP_H__
#define __RCAR_DU_GROUP_H__ #define __RCAR_DU_GROUP_H__
#include <linux/mutex.h>
#include "rcar_du_plane.h" #include "rcar_du_plane.h"
struct rcar_du_device; struct rcar_du_device;
...@@ -25,6 +27,7 @@ struct rcar_du_device; ...@@ -25,6 +27,7 @@ struct rcar_du_device;
* @index: group index * @index: group index
* @use_count: number of users of the group (rcar_du_group_(get|put)) * @use_count: number of users of the group (rcar_du_group_(get|put))
* @used_crtcs: number of CRTCs currently in use * @used_crtcs: number of CRTCs currently in use
* @lock: protects the DPTSR register
* @planes: planes handled by the group * @planes: planes handled by the group
*/ */
struct rcar_du_group { struct rcar_du_group {
...@@ -35,6 +38,8 @@ struct rcar_du_group { ...@@ -35,6 +38,8 @@ struct rcar_du_group {
unsigned int use_count; unsigned int use_count;
unsigned int used_crtcs; unsigned int used_crtcs;
struct mutex lock;
struct rcar_du_planes planes; struct rcar_du_planes planes;
}; };
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
*/ */
#include <drm/drmP.h> #include <drm/drmP.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc.h> #include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h> #include <drm/drm_crtc_helper.h>
#include <drm/drm_encoder_slave.h> #include <drm/drm_encoder_slave.h>
...@@ -74,10 +75,13 @@ rcar_du_hdmi_connector_detect(struct drm_connector *connector, bool force) ...@@ -74,10 +75,13 @@ rcar_du_hdmi_connector_detect(struct drm_connector *connector, bool force)
} }
static const struct drm_connector_funcs connector_funcs = { static const struct drm_connector_funcs connector_funcs = {
.dpms = drm_helper_connector_dpms, .dpms = drm_atomic_helper_connector_dpms,
.reset = drm_atomic_helper_connector_reset,
.detect = rcar_du_hdmi_connector_detect, .detect = rcar_du_hdmi_connector_detect,
.fill_modes = drm_helper_probe_single_connector_modes, .fill_modes = drm_helper_probe_single_connector_modes,
.destroy = rcar_du_hdmi_connector_destroy, .destroy = rcar_du_hdmi_connector_destroy,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
}; };
int rcar_du_hdmi_connector_init(struct rcar_du_device *rcdu, int rcar_du_hdmi_connector_init(struct rcar_du_device *rcdu,
...@@ -108,7 +112,7 @@ int rcar_du_hdmi_connector_init(struct rcar_du_device *rcdu, ...@@ -108,7 +112,7 @@ int rcar_du_hdmi_connector_init(struct rcar_du_device *rcdu,
if (ret < 0) if (ret < 0)
return ret; return ret;
drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF); connector->dpms = DRM_MODE_DPMS_OFF;
drm_object_property_set_value(&connector->base, drm_object_property_set_value(&connector->base,
rcdu->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF); rcdu->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF);
...@@ -116,7 +120,6 @@ int rcar_du_hdmi_connector_init(struct rcar_du_device *rcdu, ...@@ -116,7 +120,6 @@ int rcar_du_hdmi_connector_init(struct rcar_du_device *rcdu,
if (ret < 0) if (ret < 0)
return ret; return ret;
connector->encoder = encoder;
rcon->encoder = renc; rcon->encoder = renc;
return 0; return 0;
......
...@@ -26,41 +26,50 @@ ...@@ -26,41 +26,50 @@
struct rcar_du_hdmienc { struct rcar_du_hdmienc {
struct rcar_du_encoder *renc; struct rcar_du_encoder *renc;
struct device *dev; struct device *dev;
int dpms; bool enabled;
}; };
#define to_rcar_hdmienc(e) (to_rcar_encoder(e)->hdmi) #define to_rcar_hdmienc(e) (to_rcar_encoder(e)->hdmi)
#define to_slave_funcs(e) (to_rcar_encoder(e)->slave.slave_funcs) #define to_slave_funcs(e) (to_rcar_encoder(e)->slave.slave_funcs)
static void rcar_du_hdmienc_dpms(struct drm_encoder *encoder, int mode) static void rcar_du_hdmienc_disable(struct drm_encoder *encoder)
{ {
struct rcar_du_hdmienc *hdmienc = to_rcar_hdmienc(encoder); struct rcar_du_hdmienc *hdmienc = to_rcar_hdmienc(encoder);
struct drm_encoder_slave_funcs *sfuncs = to_slave_funcs(encoder); struct drm_encoder_slave_funcs *sfuncs = to_slave_funcs(encoder);
if (mode != DRM_MODE_DPMS_ON) if (sfuncs->dpms)
mode = DRM_MODE_DPMS_OFF; sfuncs->dpms(encoder, DRM_MODE_DPMS_OFF);
if (hdmienc->dpms == mode) if (hdmienc->renc->lvds)
return; rcar_du_lvdsenc_enable(hdmienc->renc->lvds, encoder->crtc,
false);
if (mode == DRM_MODE_DPMS_ON && hdmienc->renc->lvds) hdmienc->enabled = false;
rcar_du_lvdsenc_dpms(hdmienc->renc->lvds, encoder->crtc, mode); }
if (sfuncs->dpms) static void rcar_du_hdmienc_enable(struct drm_encoder *encoder)
sfuncs->dpms(encoder, mode); {
struct rcar_du_hdmienc *hdmienc = to_rcar_hdmienc(encoder);
struct drm_encoder_slave_funcs *sfuncs = to_slave_funcs(encoder);
if (hdmienc->renc->lvds)
rcar_du_lvdsenc_enable(hdmienc->renc->lvds, encoder->crtc,
true);
if (mode != DRM_MODE_DPMS_ON && hdmienc->renc->lvds) if (sfuncs->dpms)
rcar_du_lvdsenc_dpms(hdmienc->renc->lvds, encoder->crtc, mode); sfuncs->dpms(encoder, DRM_MODE_DPMS_ON);
hdmienc->dpms = mode; hdmienc->enabled = true;
} }
static bool rcar_du_hdmienc_mode_fixup(struct drm_encoder *encoder, static int rcar_du_hdmienc_atomic_check(struct drm_encoder *encoder,
const struct drm_display_mode *mode, struct drm_crtc_state *crtc_state,
struct drm_display_mode *adjusted_mode) struct drm_connector_state *conn_state)
{ {
struct rcar_du_hdmienc *hdmienc = to_rcar_hdmienc(encoder); struct rcar_du_hdmienc *hdmienc = to_rcar_hdmienc(encoder);
struct drm_encoder_slave_funcs *sfuncs = to_slave_funcs(encoder); struct drm_encoder_slave_funcs *sfuncs = to_slave_funcs(encoder);
struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode;
const struct drm_display_mode *mode = &crtc_state->mode;
/* The internal LVDS encoder has a clock frequency operating range of /* The internal LVDS encoder has a clock frequency operating range of
* 30MHz to 150MHz. Clamp the clock accordingly. * 30MHz to 150MHz. Clamp the clock accordingly.
...@@ -70,19 +79,9 @@ static bool rcar_du_hdmienc_mode_fixup(struct drm_encoder *encoder, ...@@ -70,19 +79,9 @@ static bool rcar_du_hdmienc_mode_fixup(struct drm_encoder *encoder,
30000, 150000); 30000, 150000);
if (sfuncs->mode_fixup == NULL) if (sfuncs->mode_fixup == NULL)
return true; return 0;
return sfuncs->mode_fixup(encoder, mode, adjusted_mode);
}
static void rcar_du_hdmienc_mode_prepare(struct drm_encoder *encoder) return sfuncs->mode_fixup(encoder, mode, adjusted_mode) ? 0 : -EINVAL;
{
rcar_du_hdmienc_dpms(encoder, DRM_MODE_DPMS_OFF);
}
static void rcar_du_hdmienc_mode_commit(struct drm_encoder *encoder)
{
rcar_du_hdmienc_dpms(encoder, DRM_MODE_DPMS_ON);
} }
static void rcar_du_hdmienc_mode_set(struct drm_encoder *encoder, static void rcar_du_hdmienc_mode_set(struct drm_encoder *encoder,
...@@ -99,18 +98,18 @@ static void rcar_du_hdmienc_mode_set(struct drm_encoder *encoder, ...@@ -99,18 +98,18 @@ static void rcar_du_hdmienc_mode_set(struct drm_encoder *encoder,
} }
static const struct drm_encoder_helper_funcs encoder_helper_funcs = { static const struct drm_encoder_helper_funcs encoder_helper_funcs = {
.dpms = rcar_du_hdmienc_dpms,
.mode_fixup = rcar_du_hdmienc_mode_fixup,
.prepare = rcar_du_hdmienc_mode_prepare,
.commit = rcar_du_hdmienc_mode_commit,
.mode_set = rcar_du_hdmienc_mode_set, .mode_set = rcar_du_hdmienc_mode_set,
.disable = rcar_du_hdmienc_disable,
.enable = rcar_du_hdmienc_enable,
.atomic_check = rcar_du_hdmienc_atomic_check,
}; };
static void rcar_du_hdmienc_cleanup(struct drm_encoder *encoder) static void rcar_du_hdmienc_cleanup(struct drm_encoder *encoder)
{ {
struct rcar_du_hdmienc *hdmienc = to_rcar_hdmienc(encoder); struct rcar_du_hdmienc *hdmienc = to_rcar_hdmienc(encoder);
rcar_du_hdmienc_dpms(encoder, DRM_MODE_DPMS_OFF); if (hdmienc->enabled)
rcar_du_hdmienc_disable(encoder);
drm_encoder_cleanup(encoder); drm_encoder_cleanup(encoder);
put_device(hdmienc->dev); put_device(hdmienc->dev);
......
...@@ -12,12 +12,15 @@ ...@@ -12,12 +12,15 @@
*/ */
#include <drm/drmP.h> #include <drm/drmP.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc.h> #include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h> #include <drm/drm_crtc_helper.h>
#include <drm/drm_fb_cma_helper.h> #include <drm/drm_fb_cma_helper.h>
#include <drm/drm_gem_cma_helper.h> #include <drm/drm_gem_cma_helper.h>
#include <linux/of_graph.h> #include <linux/of_graph.h>
#include <linux/wait.h>
#include "rcar_du_crtc.h" #include "rcar_du_crtc.h"
#include "rcar_du_drv.h" #include "rcar_du_drv.h"
...@@ -185,9 +188,309 @@ static void rcar_du_output_poll_changed(struct drm_device *dev) ...@@ -185,9 +188,309 @@ static void rcar_du_output_poll_changed(struct drm_device *dev)
drm_fbdev_cma_hotplug_event(rcdu->fbdev); drm_fbdev_cma_hotplug_event(rcdu->fbdev);
} }
/* -----------------------------------------------------------------------------
* Atomic Check and Update
*/
/*
* Atomic hardware plane allocator
*
* The hardware plane allocator is solely based on the atomic plane states
* without keeping any external state to avoid races between .atomic_check()
* and .atomic_commit().
*
* The core idea is to avoid using a free planes bitmask that would need to be
* shared between check and commit handlers with a collective knowledge based on
* the allocated hardware plane(s) for each KMS plane. The allocator then loops
* over all plane states to compute the free planes bitmask, allocates hardware
* planes based on that bitmask, and stores the result back in the plane states.
*
* For this to work we need to access the current state of planes not touched by
* the atomic update. To ensure that it won't be modified, we need to lock all
* planes using drm_atomic_get_plane_state(). This effectively serializes atomic
* updates from .atomic_check() up to completion (when swapping the states if
* the check step has succeeded) or rollback (when freeing the states if the
* check step has failed).
*
* Allocation is performed in the .atomic_check() handler and applied
* automatically when the core swaps the old and new states.
*/
static bool rcar_du_plane_needs_realloc(struct rcar_du_plane *plane,
struct rcar_du_plane_state *state)
{
const struct rcar_du_format_info *cur_format;
cur_format = to_rcar_du_plane_state(plane->plane.state)->format;
/* Lowering the number of planes doesn't strictly require reallocation
* as the extra hardware plane will be freed when committing, but doing
* so could lead to more fragmentation.
*/
return !cur_format || cur_format->planes != state->format->planes;
}
static unsigned int rcar_du_plane_hwmask(struct rcar_du_plane_state *state)
{
unsigned int mask;
if (state->hwindex == -1)
return 0;
mask = 1 << state->hwindex;
if (state->format->planes == 2)
mask |= 1 << ((state->hwindex + 1) % 8);
return mask;
}
static int rcar_du_plane_hwalloc(unsigned int num_planes, unsigned int free)
{
unsigned int i;
for (i = 0; i < RCAR_DU_NUM_HW_PLANES; ++i) {
if (!(free & (1 << i)))
continue;
if (num_planes == 1 || free & (1 << ((i + 1) % 8)))
break;
}
return i == RCAR_DU_NUM_HW_PLANES ? -EBUSY : i;
}
static int rcar_du_atomic_check(struct drm_device *dev,
struct drm_atomic_state *state)
{
struct rcar_du_device *rcdu = dev->dev_private;
unsigned int group_freed_planes[RCAR_DU_MAX_GROUPS] = { 0, };
unsigned int group_free_planes[RCAR_DU_MAX_GROUPS] = { 0, };
bool needs_realloc = false;
unsigned int groups = 0;
unsigned int i;
int ret;
ret = drm_atomic_helper_check(dev, state);
if (ret < 0)
return ret;
/* Check if hardware planes need to be reallocated. */
for (i = 0; i < dev->mode_config.num_total_plane; ++i) {
struct rcar_du_plane_state *plane_state;
struct rcar_du_plane *plane;
unsigned int index;
if (!state->planes[i])
continue;
plane = to_rcar_plane(state->planes[i]);
plane_state = to_rcar_du_plane_state(state->plane_states[i]);
/* If the plane is being disabled we don't need to go through
* the full reallocation procedure. Just mark the hardware
* plane(s) as freed.
*/
if (!plane_state->format) {
index = plane - plane->group->planes.planes;
group_freed_planes[plane->group->index] |= 1 << index;
plane_state->hwindex = -1;
continue;
}
/* If the plane needs to be reallocated mark it as such, and
* mark the hardware plane(s) as free.
*/
if (rcar_du_plane_needs_realloc(plane, plane_state)) {
groups |= 1 << plane->group->index;
needs_realloc = true;
index = plane - plane->group->planes.planes;
group_freed_planes[plane->group->index] |= 1 << index;
plane_state->hwindex = -1;
}
}
if (!needs_realloc)
return 0;
/* Grab all plane states for the groups that need reallocation to ensure
* locking and avoid racy updates. This serializes the update operation,
* but there's not much we can do about it as that's the hardware
* design.
*
* Compute the used planes mask for each group at the same time to avoid
* looping over the planes separately later.
*/
while (groups) {
unsigned int index = ffs(groups) - 1;
struct rcar_du_group *group = &rcdu->groups[index];
unsigned int used_planes = 0;
for (i = 0; i < RCAR_DU_NUM_KMS_PLANES; ++i) {
struct rcar_du_plane *plane = &group->planes.planes[i];
struct rcar_du_plane_state *plane_state;
struct drm_plane_state *s;
s = drm_atomic_get_plane_state(state, &plane->plane);
if (IS_ERR(s))
return PTR_ERR(s);
/* If the plane has been freed in the above loop its
* hardware planes must not be added to the used planes
* bitmask. However, the current state doesn't reflect
* the free state yet, as we've modified the new state
* above. Use the local freed planes list to check for
* that condition instead.
*/
if (group_freed_planes[index] & (1 << i))
continue;
plane_state = to_rcar_du_plane_state(plane->plane.state);
used_planes |= rcar_du_plane_hwmask(plane_state);
}
group_free_planes[index] = 0xff & ~used_planes;
groups &= ~(1 << index);
}
/* Reallocate hardware planes for each plane that needs it. */
for (i = 0; i < dev->mode_config.num_total_plane; ++i) {
struct rcar_du_plane_state *plane_state;
struct rcar_du_plane *plane;
int idx;
if (!state->planes[i])
continue;
plane = to_rcar_plane(state->planes[i]);
plane_state = to_rcar_du_plane_state(state->plane_states[i]);
/* Skip planes that are being disabled or don't need to be
* reallocated.
*/
if (!plane_state->format ||
!rcar_du_plane_needs_realloc(plane, plane_state))
continue;
idx = rcar_du_plane_hwalloc(plane_state->format->planes,
group_free_planes[plane->group->index]);
if (idx < 0) {
dev_dbg(rcdu->dev, "%s: no available hardware plane\n",
__func__);
return idx;
}
plane_state->hwindex = idx;
group_free_planes[plane->group->index] &=
~rcar_du_plane_hwmask(plane_state);
}
return 0;
}
struct rcar_du_commit {
struct work_struct work;
struct drm_device *dev;
struct drm_atomic_state *state;
u32 crtcs;
};
static void rcar_du_atomic_complete(struct rcar_du_commit *commit)
{
struct drm_device *dev = commit->dev;
struct rcar_du_device *rcdu = dev->dev_private;
struct drm_atomic_state *old_state = commit->state;
/* Apply the atomic update. */
drm_atomic_helper_commit_modeset_disables(dev, old_state);
drm_atomic_helper_commit_modeset_enables(dev, old_state);
drm_atomic_helper_commit_planes(dev, old_state);
drm_atomic_helper_wait_for_vblanks(dev, old_state);
drm_atomic_helper_cleanup_planes(dev, old_state);
drm_atomic_state_free(old_state);
/* Complete the commit, wake up any waiter. */
spin_lock(&rcdu->commit.wait.lock);
rcdu->commit.pending &= ~commit->crtcs;
wake_up_all_locked(&rcdu->commit.wait);
spin_unlock(&rcdu->commit.wait.lock);
kfree(commit);
}
static void rcar_du_atomic_work(struct work_struct *work)
{
struct rcar_du_commit *commit =
container_of(work, struct rcar_du_commit, work);
rcar_du_atomic_complete(commit);
}
static int rcar_du_atomic_commit(struct drm_device *dev,
struct drm_atomic_state *state, bool async)
{
struct rcar_du_device *rcdu = dev->dev_private;
struct rcar_du_commit *commit;
unsigned int i;
int ret;
ret = drm_atomic_helper_prepare_planes(dev, state);
if (ret)
return ret;
/* Allocate the commit object. */
commit = kzalloc(sizeof(*commit), GFP_KERNEL);
if (commit == NULL)
return -ENOMEM;
INIT_WORK(&commit->work, rcar_du_atomic_work);
commit->dev = dev;
commit->state = state;
/* Wait until all affected CRTCs have completed previous commits and
* mark them as pending.
*/
for (i = 0; i < dev->mode_config.num_crtc; ++i) {
if (state->crtcs[i])
commit->crtcs |= 1 << drm_crtc_index(state->crtcs[i]);
}
spin_lock(&rcdu->commit.wait.lock);
ret = wait_event_interruptible_locked(rcdu->commit.wait,
!(rcdu->commit.pending & commit->crtcs));
if (ret == 0)
rcdu->commit.pending |= commit->crtcs;
spin_unlock(&rcdu->commit.wait.lock);
if (ret) {
kfree(commit);
return ret;
}
/* Swap the state, this is the point of no return. */
drm_atomic_helper_swap_state(dev, state);
if (async)
schedule_work(&commit->work);
else
rcar_du_atomic_complete(commit);
return 0;
}
/* -----------------------------------------------------------------------------
* Initialization
*/
static const struct drm_mode_config_funcs rcar_du_mode_config_funcs = { static const struct drm_mode_config_funcs rcar_du_mode_config_funcs = {
.fb_create = rcar_du_fb_create, .fb_create = rcar_du_fb_create,
.output_poll_changed = rcar_du_output_poll_changed, .output_poll_changed = rcar_du_output_poll_changed,
.atomic_check = rcar_du_atomic_check,
.atomic_commit = rcar_du_atomic_commit,
}; };
static int rcar_du_encoders_init_one(struct rcar_du_device *rcdu, static int rcar_du_encoders_init_one(struct rcar_du_device *rcdu,
...@@ -392,6 +695,8 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu) ...@@ -392,6 +695,8 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu)
for (i = 0; i < num_groups; ++i) { for (i = 0; i < num_groups; ++i) {
struct rcar_du_group *rgrp = &rcdu->groups[i]; struct rcar_du_group *rgrp = &rcdu->groups[i];
mutex_init(&rgrp->lock);
rgrp->dev = rcdu; rgrp->dev = rcdu;
rgrp->mmio_offset = mmio_offsets[i]; rgrp->mmio_offset = mmio_offsets[i];
rgrp->index = i; rgrp->index = i;
...@@ -439,27 +744,21 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu) ...@@ -439,27 +744,21 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu)
encoder->possible_clones = (1 << num_encoders) - 1; encoder->possible_clones = (1 << num_encoders) - 1;
} }
/* Now that the CRTCs have been initialized register the planes. */ drm_mode_config_reset(dev);
for (i = 0; i < num_groups; ++i) {
ret = rcar_du_planes_register(&rcdu->groups[i]);
if (ret < 0)
return ret;
}
drm_kms_helper_poll_init(dev); drm_kms_helper_poll_init(dev);
drm_helper_disable_unused_functions(dev); if (dev->mode_config.num_connector) {
fbdev = drm_fbdev_cma_init(dev, 32, dev->mode_config.num_crtc,
fbdev = drm_fbdev_cma_init(dev, 32, dev->mode_config.num_crtc, dev->mode_config.num_connector);
dev->mode_config.num_connector); if (IS_ERR(fbdev))
if (IS_ERR(fbdev)) return PTR_ERR(fbdev);
return PTR_ERR(fbdev);
#ifndef CONFIG_FRAMEBUFFER_CONSOLE rcdu->fbdev = fbdev;
drm_fbdev_cma_restore_mode(fbdev); } else {
#endif dev_info(rcdu->dev,
"no connector found, disabling fbdev emulation\n");
rcdu->fbdev = fbdev; }
return 0; return 0;
} }
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
*/ */
#include <drm/drmP.h> #include <drm/drmP.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc.h> #include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h> #include <drm/drm_crtc_helper.h>
...@@ -74,10 +75,13 @@ rcar_du_lvds_connector_detect(struct drm_connector *connector, bool force) ...@@ -74,10 +75,13 @@ rcar_du_lvds_connector_detect(struct drm_connector *connector, bool force)
} }
static const struct drm_connector_funcs connector_funcs = { static const struct drm_connector_funcs connector_funcs = {
.dpms = drm_helper_connector_dpms, .dpms = drm_atomic_helper_connector_dpms,
.reset = drm_atomic_helper_connector_reset,
.detect = rcar_du_lvds_connector_detect, .detect = rcar_du_lvds_connector_detect,
.fill_modes = drm_helper_probe_single_connector_modes, .fill_modes = drm_helper_probe_single_connector_modes,
.destroy = rcar_du_lvds_connector_destroy, .destroy = rcar_du_lvds_connector_destroy,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
}; };
int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu, int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu,
...@@ -117,7 +121,7 @@ int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu, ...@@ -117,7 +121,7 @@ int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu,
if (ret < 0) if (ret < 0)
return ret; return ret;
drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF); connector->dpms = DRM_MODE_DPMS_OFF;
drm_object_property_set_value(&connector->base, drm_object_property_set_value(&connector->base,
rcdu->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF); rcdu->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF);
...@@ -125,7 +129,6 @@ int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu, ...@@ -125,7 +129,6 @@ int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu,
if (ret < 0) if (ret < 0)
return ret; return ret;
connector->encoder = encoder;
lvdscon->connector.encoder = renc; lvdscon->connector.encoder = renc;
return 0; return 0;
......
...@@ -28,7 +28,7 @@ struct rcar_du_lvdsenc { ...@@ -28,7 +28,7 @@ struct rcar_du_lvdsenc {
unsigned int index; unsigned int index;
void __iomem *mmio; void __iomem *mmio;
struct clk *clock; struct clk *clock;
int dpms; bool enabled;
enum rcar_lvds_input input; enum rcar_lvds_input input;
}; };
...@@ -48,7 +48,7 @@ static int rcar_du_lvdsenc_start(struct rcar_du_lvdsenc *lvds, ...@@ -48,7 +48,7 @@ static int rcar_du_lvdsenc_start(struct rcar_du_lvdsenc *lvds,
u32 pllcr; u32 pllcr;
int ret; int ret;
if (lvds->dpms == DRM_MODE_DPMS_ON) if (lvds->enabled)
return 0; return 0;
ret = clk_prepare_enable(lvds->clock); ret = clk_prepare_enable(lvds->clock);
...@@ -110,13 +110,13 @@ static int rcar_du_lvdsenc_start(struct rcar_du_lvdsenc *lvds, ...@@ -110,13 +110,13 @@ static int rcar_du_lvdsenc_start(struct rcar_du_lvdsenc *lvds,
lvdcr0 |= LVDCR0_LVRES; lvdcr0 |= LVDCR0_LVRES;
rcar_lvds_write(lvds, LVDCR0, lvdcr0); rcar_lvds_write(lvds, LVDCR0, lvdcr0);
lvds->dpms = DRM_MODE_DPMS_ON; lvds->enabled = true;
return 0; return 0;
} }
static void rcar_du_lvdsenc_stop(struct rcar_du_lvdsenc *lvds) static void rcar_du_lvdsenc_stop(struct rcar_du_lvdsenc *lvds)
{ {
if (lvds->dpms == DRM_MODE_DPMS_OFF) if (!lvds->enabled)
return; return;
rcar_lvds_write(lvds, LVDCR0, 0); rcar_lvds_write(lvds, LVDCR0, 0);
...@@ -124,13 +124,13 @@ static void rcar_du_lvdsenc_stop(struct rcar_du_lvdsenc *lvds) ...@@ -124,13 +124,13 @@ static void rcar_du_lvdsenc_stop(struct rcar_du_lvdsenc *lvds)
clk_disable_unprepare(lvds->clock); clk_disable_unprepare(lvds->clock);
lvds->dpms = DRM_MODE_DPMS_OFF; lvds->enabled = false;
} }
int rcar_du_lvdsenc_dpms(struct rcar_du_lvdsenc *lvds, int rcar_du_lvdsenc_enable(struct rcar_du_lvdsenc *lvds, struct drm_crtc *crtc,
struct drm_crtc *crtc, int mode) bool enable)
{ {
if (mode == DRM_MODE_DPMS_OFF) { if (!enable) {
rcar_du_lvdsenc_stop(lvds); rcar_du_lvdsenc_stop(lvds);
return 0; return 0;
} else if (crtc) { } else if (crtc) {
...@@ -179,7 +179,7 @@ int rcar_du_lvdsenc_init(struct rcar_du_device *rcdu) ...@@ -179,7 +179,7 @@ int rcar_du_lvdsenc_init(struct rcar_du_device *rcdu)
lvds->dev = rcdu; lvds->dev = rcdu;
lvds->index = i; lvds->index = i;
lvds->input = i ? RCAR_LVDS_INPUT_DU1 : RCAR_LVDS_INPUT_DU0; lvds->input = i ? RCAR_LVDS_INPUT_DU1 : RCAR_LVDS_INPUT_DU0;
lvds->dpms = DRM_MODE_DPMS_OFF; lvds->enabled = false;
ret = rcar_du_lvdsenc_get_resources(lvds, pdev); ret = rcar_du_lvdsenc_get_resources(lvds, pdev);
if (ret < 0) if (ret < 0)
......
...@@ -28,15 +28,15 @@ enum rcar_lvds_input { ...@@ -28,15 +28,15 @@ enum rcar_lvds_input {
#if IS_ENABLED(CONFIG_DRM_RCAR_LVDS) #if IS_ENABLED(CONFIG_DRM_RCAR_LVDS)
int rcar_du_lvdsenc_init(struct rcar_du_device *rcdu); int rcar_du_lvdsenc_init(struct rcar_du_device *rcdu);
int rcar_du_lvdsenc_dpms(struct rcar_du_lvdsenc *lvds, int rcar_du_lvdsenc_enable(struct rcar_du_lvdsenc *lvds,
struct drm_crtc *crtc, int mode); struct drm_crtc *crtc, bool enable);
#else #else
static inline int rcar_du_lvdsenc_init(struct rcar_du_device *rcdu) static inline int rcar_du_lvdsenc_init(struct rcar_du_device *rcdu)
{ {
return 0; return 0;
} }
static inline int rcar_du_lvdsenc_dpms(struct rcar_du_lvdsenc *lvds, static inline int rcar_du_lvdsenc_enable(struct rcar_du_lvdsenc *lvds,
struct drm_crtc *crtc, int mode) struct drm_crtc *crtc, bool enable)
{ {
return 0; return 0;
} }
......
...@@ -12,10 +12,12 @@ ...@@ -12,10 +12,12 @@
*/ */
#include <drm/drmP.h> #include <drm/drmP.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc.h> #include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h> #include <drm/drm_crtc_helper.h>
#include <drm/drm_fb_cma_helper.h> #include <drm/drm_fb_cma_helper.h>
#include <drm/drm_gem_cma_helper.h> #include <drm/drm_gem_cma_helper.h>
#include <drm/drm_plane_helper.h>
#include "rcar_du_drv.h" #include "rcar_du_drv.h"
#include "rcar_du_kms.h" #include "rcar_du_kms.h"
...@@ -26,16 +28,6 @@ ...@@ -26,16 +28,6 @@
#define RCAR_DU_COLORKEY_SOURCE (1 << 24) #define RCAR_DU_COLORKEY_SOURCE (1 << 24)
#define RCAR_DU_COLORKEY_MASK (1 << 24) #define RCAR_DU_COLORKEY_MASK (1 << 24)
struct rcar_du_kms_plane {
struct drm_plane plane;
struct rcar_du_plane *hwplane;
};
static inline struct rcar_du_plane *to_rcar_plane(struct drm_plane *plane)
{
return container_of(plane, struct rcar_du_kms_plane, plane)->hwplane;
}
static u32 rcar_du_plane_read(struct rcar_du_group *rgrp, static u32 rcar_du_plane_read(struct rcar_du_group *rgrp,
unsigned int index, u32 reg) unsigned int index, u32 reg)
{ {
...@@ -50,74 +42,31 @@ static void rcar_du_plane_write(struct rcar_du_group *rgrp, ...@@ -50,74 +42,31 @@ static void rcar_du_plane_write(struct rcar_du_group *rgrp,
data); data);
} }
int rcar_du_plane_reserve(struct rcar_du_plane *plane, static void rcar_du_plane_setup_fb(struct rcar_du_plane *plane)
const struct rcar_du_format_info *format)
{
struct rcar_du_group *rgrp = plane->group;
unsigned int i;
int ret = -EBUSY;
mutex_lock(&rgrp->planes.lock);
for (i = 0; i < ARRAY_SIZE(rgrp->planes.planes); ++i) {
if (!(rgrp->planes.free & (1 << i)))
continue;
if (format->planes == 1 ||
rgrp->planes.free & (1 << ((i + 1) % 8)))
break;
}
if (i == ARRAY_SIZE(rgrp->planes.planes))
goto done;
rgrp->planes.free &= ~(1 << i);
if (format->planes == 2)
rgrp->planes.free &= ~(1 << ((i + 1) % 8));
plane->hwindex = i;
ret = 0;
done:
mutex_unlock(&rgrp->planes.lock);
return ret;
}
void rcar_du_plane_release(struct rcar_du_plane *plane)
{
struct rcar_du_group *rgrp = plane->group;
if (plane->hwindex == -1)
return;
mutex_lock(&rgrp->planes.lock);
rgrp->planes.free |= 1 << plane->hwindex;
if (plane->format->planes == 2)
rgrp->planes.free |= 1 << ((plane->hwindex + 1) % 8);
mutex_unlock(&rgrp->planes.lock);
plane->hwindex = -1;
}
void rcar_du_plane_update_base(struct rcar_du_plane *plane)
{ {
struct rcar_du_plane_state *state =
to_rcar_du_plane_state(plane->plane.state);
struct drm_framebuffer *fb = plane->plane.state->fb;
struct rcar_du_group *rgrp = plane->group; struct rcar_du_group *rgrp = plane->group;
unsigned int index = plane->hwindex; unsigned int src_x = state->state.src_x >> 16;
unsigned int src_y = state->state.src_y >> 16;
unsigned int index = state->hwindex;
struct drm_gem_cma_object *gem;
bool interlaced; bool interlaced;
u32 mwr; u32 mwr;
interlaced = plane->crtc->mode.flags & DRM_MODE_FLAG_INTERLACE; interlaced = state->state.crtc->state->adjusted_mode.flags
& DRM_MODE_FLAG_INTERLACE;
/* Memory pitch (expressed in pixels). Must be doubled for interlaced /* Memory pitch (expressed in pixels). Must be doubled for interlaced
* operation with 32bpp formats. * operation with 32bpp formats.
*/ */
if (plane->format->planes == 2) if (state->format->planes == 2)
mwr = plane->pitch; mwr = fb->pitches[0];
else else
mwr = plane->pitch * 8 / plane->format->bpp; mwr = fb->pitches[0] * 8 / state->format->bpp;
if (interlaced && plane->format->bpp == 32) if (interlaced && state->format->bpp == 32)
mwr *= 2; mwr *= 2;
rcar_du_plane_write(rgrp, index, PnMWR, mwr); rcar_du_plane_write(rgrp, index, PnMWR, mwr);
...@@ -134,42 +83,33 @@ void rcar_du_plane_update_base(struct rcar_du_plane *plane) ...@@ -134,42 +83,33 @@ void rcar_du_plane_update_base(struct rcar_du_plane *plane)
* require a halved Y position value, in both progressive and interlaced * require a halved Y position value, in both progressive and interlaced
* modes. * modes.
*/ */
rcar_du_plane_write(rgrp, index, PnSPXR, plane->src_x); rcar_du_plane_write(rgrp, index, PnSPXR, src_x);
rcar_du_plane_write(rgrp, index, PnSPYR, plane->src_y * rcar_du_plane_write(rgrp, index, PnSPYR, src_y *
(!interlaced && plane->format->bpp == 32 ? 2 : 1)); (!interlaced && state->format->bpp == 32 ? 2 : 1));
rcar_du_plane_write(rgrp, index, PnDSA0R, plane->dma[0]);
if (plane->format->planes == 2) { gem = drm_fb_cma_get_gem_obj(fb, 0);
index = (index + 1) % 8; rcar_du_plane_write(rgrp, index, PnDSA0R, gem->paddr + fb->offsets[0]);
rcar_du_plane_write(rgrp, index, PnMWR, plane->pitch);
rcar_du_plane_write(rgrp, index, PnSPXR, plane->src_x); if (state->format->planes == 2) {
rcar_du_plane_write(rgrp, index, PnSPYR, plane->src_y * index = (index + 1) % 8;
(plane->format->bpp == 16 ? 2 : 1) / 2);
rcar_du_plane_write(rgrp, index, PnDSA0R, plane->dma[1]);
}
}
void rcar_du_plane_compute_base(struct rcar_du_plane *plane, rcar_du_plane_write(rgrp, index, PnMWR, fb->pitches[0]);
struct drm_framebuffer *fb)
{
struct drm_gem_cma_object *gem;
plane->pitch = fb->pitches[0]; rcar_du_plane_write(rgrp, index, PnSPXR, src_x);
rcar_du_plane_write(rgrp, index, PnSPYR, src_y *
(state->format->bpp == 16 ? 2 : 1) / 2);
gem = drm_fb_cma_get_gem_obj(fb, 0);
plane->dma[0] = gem->paddr + fb->offsets[0];
if (plane->format->planes == 2) {
gem = drm_fb_cma_get_gem_obj(fb, 1); gem = drm_fb_cma_get_gem_obj(fb, 1);
plane->dma[1] = gem->paddr + fb->offsets[1]; rcar_du_plane_write(rgrp, index, PnDSA0R,
gem->paddr + fb->offsets[1]);
} }
} }
static void rcar_du_plane_setup_mode(struct rcar_du_plane *plane, static void rcar_du_plane_setup_mode(struct rcar_du_plane *plane,
unsigned int index) unsigned int index)
{ {
struct rcar_du_plane_state *state =
to_rcar_du_plane_state(plane->plane.state);
struct rcar_du_group *rgrp = plane->group; struct rcar_du_group *rgrp = plane->group;
u32 colorkey; u32 colorkey;
u32 pnmr; u32 pnmr;
...@@ -183,47 +123,47 @@ static void rcar_du_plane_setup_mode(struct rcar_du_plane *plane, ...@@ -183,47 +123,47 @@ static void rcar_du_plane_setup_mode(struct rcar_du_plane *plane,
* For XRGB, set the alpha value to the plane-wide alpha value and * For XRGB, set the alpha value to the plane-wide alpha value and
* enable alpha-blending regardless of the X bit value. * enable alpha-blending regardless of the X bit value.
*/ */
if (plane->format->fourcc != DRM_FORMAT_XRGB1555) if (state->format->fourcc != DRM_FORMAT_XRGB1555)
rcar_du_plane_write(rgrp, index, PnALPHAR, PnALPHAR_ABIT_0); rcar_du_plane_write(rgrp, index, PnALPHAR, PnALPHAR_ABIT_0);
else else
rcar_du_plane_write(rgrp, index, PnALPHAR, rcar_du_plane_write(rgrp, index, PnALPHAR,
PnALPHAR_ABIT_X | plane->alpha); PnALPHAR_ABIT_X | state->alpha);
pnmr = PnMR_BM_MD | plane->format->pnmr; pnmr = PnMR_BM_MD | state->format->pnmr;
/* Disable color keying when requested. YUV formats have the /* Disable color keying when requested. YUV formats have the
* PnMR_SPIM_TP_OFF bit set in their pnmr field, disabling color keying * PnMR_SPIM_TP_OFF bit set in their pnmr field, disabling color keying
* automatically. * automatically.
*/ */
if ((plane->colorkey & RCAR_DU_COLORKEY_MASK) == RCAR_DU_COLORKEY_NONE) if ((state->colorkey & RCAR_DU_COLORKEY_MASK) == RCAR_DU_COLORKEY_NONE)
pnmr |= PnMR_SPIM_TP_OFF; pnmr |= PnMR_SPIM_TP_OFF;
/* For packed YUV formats we need to select the U/V order. */ /* For packed YUV formats we need to select the U/V order. */
if (plane->format->fourcc == DRM_FORMAT_YUYV) if (state->format->fourcc == DRM_FORMAT_YUYV)
pnmr |= PnMR_YCDF_YUYV; pnmr |= PnMR_YCDF_YUYV;
rcar_du_plane_write(rgrp, index, PnMR, pnmr); rcar_du_plane_write(rgrp, index, PnMR, pnmr);
switch (plane->format->fourcc) { switch (state->format->fourcc) {
case DRM_FORMAT_RGB565: case DRM_FORMAT_RGB565:
colorkey = ((plane->colorkey & 0xf80000) >> 8) colorkey = ((state->colorkey & 0xf80000) >> 8)
| ((plane->colorkey & 0x00fc00) >> 5) | ((state->colorkey & 0x00fc00) >> 5)
| ((plane->colorkey & 0x0000f8) >> 3); | ((state->colorkey & 0x0000f8) >> 3);
rcar_du_plane_write(rgrp, index, PnTC2R, colorkey); rcar_du_plane_write(rgrp, index, PnTC2R, colorkey);
break; break;
case DRM_FORMAT_ARGB1555: case DRM_FORMAT_ARGB1555:
case DRM_FORMAT_XRGB1555: case DRM_FORMAT_XRGB1555:
colorkey = ((plane->colorkey & 0xf80000) >> 9) colorkey = ((state->colorkey & 0xf80000) >> 9)
| ((plane->colorkey & 0x00f800) >> 6) | ((state->colorkey & 0x00f800) >> 6)
| ((plane->colorkey & 0x0000f8) >> 3); | ((state->colorkey & 0x0000f8) >> 3);
rcar_du_plane_write(rgrp, index, PnTC2R, colorkey); rcar_du_plane_write(rgrp, index, PnTC2R, colorkey);
break; break;
case DRM_FORMAT_XRGB8888: case DRM_FORMAT_XRGB8888:
case DRM_FORMAT_ARGB8888: case DRM_FORMAT_ARGB8888:
rcar_du_plane_write(rgrp, index, PnTC3R, rcar_du_plane_write(rgrp, index, PnTC3R,
PnTC3R_CODE | (plane->colorkey & 0xffffff)); PnTC3R_CODE | (state->colorkey & 0xffffff));
break; break;
} }
} }
...@@ -231,6 +171,8 @@ static void rcar_du_plane_setup_mode(struct rcar_du_plane *plane, ...@@ -231,6 +171,8 @@ static void rcar_du_plane_setup_mode(struct rcar_du_plane *plane,
static void __rcar_du_plane_setup(struct rcar_du_plane *plane, static void __rcar_du_plane_setup(struct rcar_du_plane *plane,
unsigned int index) unsigned int index)
{ {
struct rcar_du_plane_state *state =
to_rcar_du_plane_state(plane->plane.state);
struct rcar_du_group *rgrp = plane->group; struct rcar_du_group *rgrp = plane->group;
u32 ddcr2 = PnDDCR2_CODE; u32 ddcr2 = PnDDCR2_CODE;
u32 ddcr4; u32 ddcr4;
...@@ -242,17 +184,17 @@ static void __rcar_du_plane_setup(struct rcar_du_plane *plane, ...@@ -242,17 +184,17 @@ static void __rcar_du_plane_setup(struct rcar_du_plane *plane,
*/ */
ddcr4 = rcar_du_plane_read(rgrp, index, PnDDCR4); ddcr4 = rcar_du_plane_read(rgrp, index, PnDDCR4);
ddcr4 &= ~PnDDCR4_EDF_MASK; ddcr4 &= ~PnDDCR4_EDF_MASK;
ddcr4 |= plane->format->edf | PnDDCR4_CODE; ddcr4 |= state->format->edf | PnDDCR4_CODE;
rcar_du_plane_setup_mode(plane, index); rcar_du_plane_setup_mode(plane, index);
if (plane->format->planes == 2) { if (state->format->planes == 2) {
if (plane->hwindex != index) { if (state->hwindex != index) {
if (plane->format->fourcc == DRM_FORMAT_NV12 || if (state->format->fourcc == DRM_FORMAT_NV12 ||
plane->format->fourcc == DRM_FORMAT_NV21) state->format->fourcc == DRM_FORMAT_NV21)
ddcr2 |= PnDDCR2_Y420; ddcr2 |= PnDDCR2_Y420;
if (plane->format->fourcc == DRM_FORMAT_NV21) if (state->format->fourcc == DRM_FORMAT_NV21)
ddcr2 |= PnDDCR2_NV21; ddcr2 |= PnDDCR2_NV21;
ddcr2 |= PnDDCR2_DIVU; ddcr2 |= PnDDCR2_DIVU;
...@@ -265,10 +207,10 @@ static void __rcar_du_plane_setup(struct rcar_du_plane *plane, ...@@ -265,10 +207,10 @@ static void __rcar_du_plane_setup(struct rcar_du_plane *plane,
rcar_du_plane_write(rgrp, index, PnDDCR4, ddcr4); rcar_du_plane_write(rgrp, index, PnDDCR4, ddcr4);
/* Destination position and size */ /* Destination position and size */
rcar_du_plane_write(rgrp, index, PnDSXR, plane->width); rcar_du_plane_write(rgrp, index, PnDSXR, plane->plane.state->crtc_w);
rcar_du_plane_write(rgrp, index, PnDSYR, plane->height); rcar_du_plane_write(rgrp, index, PnDSYR, plane->plane.state->crtc_h);
rcar_du_plane_write(rgrp, index, PnDPXR, plane->dst_x); rcar_du_plane_write(rgrp, index, PnDPXR, plane->plane.state->crtc_x);
rcar_du_plane_write(rgrp, index, PnDPYR, plane->dst_y); rcar_du_plane_write(rgrp, index, PnDPYR, plane->plane.state->crtc_y);
/* Wrap-around and blinking, disabled */ /* Wrap-around and blinking, disabled */
rcar_du_plane_write(rgrp, index, PnWASPR, 0); rcar_du_plane_write(rgrp, index, PnWASPR, 0);
...@@ -279,150 +221,140 @@ static void __rcar_du_plane_setup(struct rcar_du_plane *plane, ...@@ -279,150 +221,140 @@ static void __rcar_du_plane_setup(struct rcar_du_plane *plane,
void rcar_du_plane_setup(struct rcar_du_plane *plane) void rcar_du_plane_setup(struct rcar_du_plane *plane)
{ {
__rcar_du_plane_setup(plane, plane->hwindex); struct rcar_du_plane_state *state =
if (plane->format->planes == 2) to_rcar_du_plane_state(plane->plane.state);
__rcar_du_plane_setup(plane, (plane->hwindex + 1) % 8);
__rcar_du_plane_setup(plane, state->hwindex);
if (state->format->planes == 2)
__rcar_du_plane_setup(plane, (state->hwindex + 1) % 8);
rcar_du_plane_update_base(plane); rcar_du_plane_setup_fb(plane);
} }
static int static int rcar_du_plane_atomic_check(struct drm_plane *plane,
rcar_du_plane_update(struct drm_plane *plane, struct drm_crtc *crtc, struct drm_plane_state *state)
struct drm_framebuffer *fb, int crtc_x, int crtc_y,
unsigned int crtc_w, unsigned int crtc_h,
uint32_t src_x, uint32_t src_y,
uint32_t src_w, uint32_t src_h)
{ {
struct rcar_du_plane_state *rstate = to_rcar_du_plane_state(state);
struct rcar_du_plane *rplane = to_rcar_plane(plane); struct rcar_du_plane *rplane = to_rcar_plane(plane);
struct rcar_du_device *rcdu = rplane->group->dev; struct rcar_du_device *rcdu = rplane->group->dev;
const struct rcar_du_format_info *format;
unsigned int nplanes;
int ret;
format = rcar_du_format_info(fb->pixel_format); if (!state->fb || !state->crtc) {
if (format == NULL) { rstate->format = NULL;
dev_dbg(rcdu->dev, "%s: unsupported format %08x\n", __func__, return 0;
fb->pixel_format);
return -EINVAL;
} }
if (src_w >> 16 != crtc_w || src_h >> 16 != crtc_h) { if (state->src_w >> 16 != state->crtc_w ||
state->src_h >> 16 != state->crtc_h) {
dev_dbg(rcdu->dev, "%s: scaling not supported\n", __func__); dev_dbg(rcdu->dev, "%s: scaling not supported\n", __func__);
return -EINVAL; return -EINVAL;
} }
nplanes = rplane->format ? rplane->format->planes : 0; rstate->format = rcar_du_format_info(state->fb->pixel_format);
if (rstate->format == NULL) {
/* Reallocate hardware planes if the number of required planes has dev_dbg(rcdu->dev, "%s: unsupported format %08x\n", __func__,
* changed. state->fb->pixel_format);
*/ return -EINVAL;
if (format->planes != nplanes) {
rcar_du_plane_release(rplane);
ret = rcar_du_plane_reserve(rplane, format);
if (ret < 0)
return ret;
} }
rplane->crtc = crtc;
rplane->format = format;
rplane->src_x = src_x >> 16;
rplane->src_y = src_y >> 16;
rplane->dst_x = crtc_x;
rplane->dst_y = crtc_y;
rplane->width = crtc_w;
rplane->height = crtc_h;
rcar_du_plane_compute_base(rplane, fb);
rcar_du_plane_setup(rplane);
mutex_lock(&rplane->group->planes.lock);
rplane->enabled = true;
rcar_du_crtc_update_planes(rplane->crtc);
mutex_unlock(&rplane->group->planes.lock);
return 0; return 0;
} }
static int rcar_du_plane_disable(struct drm_plane *plane) static void rcar_du_plane_atomic_update(struct drm_plane *plane,
struct drm_plane_state *old_state)
{ {
struct rcar_du_plane *rplane = to_rcar_plane(plane); struct rcar_du_plane *rplane = to_rcar_plane(plane);
if (!rplane->enabled) if (plane->state->crtc)
return 0; rcar_du_plane_setup(rplane);
}
mutex_lock(&rplane->group->planes.lock); static const struct drm_plane_helper_funcs rcar_du_plane_helper_funcs = {
rplane->enabled = false; .atomic_check = rcar_du_plane_atomic_check,
rcar_du_crtc_update_planes(rplane->crtc); .atomic_update = rcar_du_plane_atomic_update,
mutex_unlock(&rplane->group->planes.lock); };
rcar_du_plane_release(rplane); static void rcar_du_plane_reset(struct drm_plane *plane)
{
struct rcar_du_plane_state *state;
rplane->crtc = NULL; if (plane->state && plane->state->fb)
rplane->format = NULL; drm_framebuffer_unreference(plane->state->fb);
return 0; kfree(plane->state);
} plane->state = NULL;
/* Both the .set_property and the .update_plane operations are called with the state = kzalloc(sizeof(*state), GFP_KERNEL);
* mode_config lock held. There is this no need to explicitly protect access to if (state == NULL)
* the alpha and colorkey fields and the mode register.
*/
static void rcar_du_plane_set_alpha(struct rcar_du_plane *plane, u32 alpha)
{
if (plane->alpha == alpha)
return; return;
plane->alpha = alpha; state->hwindex = -1;
if (!plane->enabled || plane->format->fourcc != DRM_FORMAT_XRGB1555) state->alpha = 255;
return; state->colorkey = RCAR_DU_COLORKEY_NONE;
state->zpos = plane->type == DRM_PLANE_TYPE_PRIMARY ? 0 : 1;
rcar_du_plane_setup_mode(plane, plane->hwindex); plane->state = &state->state;
plane->state->plane = plane;
} }
static void rcar_du_plane_set_colorkey(struct rcar_du_plane *plane, static struct drm_plane_state *
u32 colorkey) rcar_du_plane_atomic_duplicate_state(struct drm_plane *plane)
{ {
if (plane->colorkey == colorkey) struct rcar_du_plane_state *state;
return; struct rcar_du_plane_state *copy;
plane->colorkey = colorkey; state = to_rcar_du_plane_state(plane->state);
if (!plane->enabled) copy = kmemdup(state, sizeof(*state), GFP_KERNEL);
return; if (copy == NULL)
return NULL;
if (copy->state.fb)
drm_framebuffer_reference(copy->state.fb);
rcar_du_plane_setup_mode(plane, plane->hwindex); return &copy->state;
} }
static void rcar_du_plane_set_zpos(struct rcar_du_plane *plane, static void rcar_du_plane_atomic_destroy_state(struct drm_plane *plane,
unsigned int zpos) struct drm_plane_state *state)
{ {
mutex_lock(&plane->group->planes.lock); kfree(to_rcar_du_plane_state(state));
if (plane->zpos == zpos) }
goto done;
plane->zpos = zpos; static int rcar_du_plane_atomic_set_property(struct drm_plane *plane,
if (!plane->enabled) struct drm_plane_state *state,
goto done; struct drm_property *property,
uint64_t val)
{
struct rcar_du_plane_state *rstate = to_rcar_du_plane_state(state);
struct rcar_du_plane *rplane = to_rcar_plane(plane);
struct rcar_du_group *rgrp = rplane->group;
rcar_du_crtc_update_planes(plane->crtc); if (property == rgrp->planes.alpha)
rstate->alpha = val;
else if (property == rgrp->planes.colorkey)
rstate->colorkey = val;
else if (property == rgrp->planes.zpos)
rstate->zpos = val;
else
return -EINVAL;
done: return 0;
mutex_unlock(&plane->group->planes.lock);
} }
static int rcar_du_plane_set_property(struct drm_plane *plane, static int rcar_du_plane_atomic_get_property(struct drm_plane *plane,
struct drm_property *property, const struct drm_plane_state *state, struct drm_property *property,
uint64_t value) uint64_t *val)
{ {
const struct rcar_du_plane_state *rstate =
container_of(state, const struct rcar_du_plane_state, state);
struct rcar_du_plane *rplane = to_rcar_plane(plane); struct rcar_du_plane *rplane = to_rcar_plane(plane);
struct rcar_du_group *rgrp = rplane->group; struct rcar_du_group *rgrp = rplane->group;
if (property == rgrp->planes.alpha) if (property == rgrp->planes.alpha)
rcar_du_plane_set_alpha(rplane, value); *val = rstate->alpha;
else if (property == rgrp->planes.colorkey) else if (property == rgrp->planes.colorkey)
rcar_du_plane_set_colorkey(rplane, value); *val = rstate->colorkey;
else if (property == rgrp->planes.zpos) else if (property == rgrp->planes.zpos)
rcar_du_plane_set_zpos(rplane, value); *val = rstate->zpos;
else else
return -EINVAL; return -EINVAL;
...@@ -430,10 +362,15 @@ static int rcar_du_plane_set_property(struct drm_plane *plane, ...@@ -430,10 +362,15 @@ static int rcar_du_plane_set_property(struct drm_plane *plane,
} }
static const struct drm_plane_funcs rcar_du_plane_funcs = { static const struct drm_plane_funcs rcar_du_plane_funcs = {
.update_plane = rcar_du_plane_update, .update_plane = drm_atomic_helper_update_plane,
.disable_plane = rcar_du_plane_disable, .disable_plane = drm_atomic_helper_disable_plane,
.set_property = rcar_du_plane_set_property, .reset = rcar_du_plane_reset,
.set_property = drm_atomic_helper_plane_set_property,
.destroy = drm_plane_cleanup, .destroy = drm_plane_cleanup,
.atomic_duplicate_state = rcar_du_plane_atomic_duplicate_state,
.atomic_destroy_state = rcar_du_plane_atomic_destroy_state,
.atomic_set_property = rcar_du_plane_atomic_set_property,
.atomic_get_property = rcar_du_plane_atomic_get_property,
}; };
static const uint32_t formats[] = { static const uint32_t formats[] = {
...@@ -453,10 +390,11 @@ int rcar_du_planes_init(struct rcar_du_group *rgrp) ...@@ -453,10 +390,11 @@ int rcar_du_planes_init(struct rcar_du_group *rgrp)
{ {
struct rcar_du_planes *planes = &rgrp->planes; struct rcar_du_planes *planes = &rgrp->planes;
struct rcar_du_device *rcdu = rgrp->dev; struct rcar_du_device *rcdu = rgrp->dev;
unsigned int num_planes;
unsigned int num_crtcs;
unsigned int crtcs;
unsigned int i; unsigned int i;
int ret;
mutex_init(&planes->lock);
planes->free = 0xff;
planes->alpha = planes->alpha =
drm_property_create_range(rcdu->ddev, 0, "alpha", 0, 255); drm_property_create_range(rcdu->ddev, 0, "alpha", 0, 255);
...@@ -478,45 +416,34 @@ int rcar_du_planes_init(struct rcar_du_group *rgrp) ...@@ -478,45 +416,34 @@ int rcar_du_planes_init(struct rcar_du_group *rgrp)
if (planes->zpos == NULL) if (planes->zpos == NULL)
return -ENOMEM; return -ENOMEM;
for (i = 0; i < ARRAY_SIZE(planes->planes); ++i) { /* Create one primary plane per in this group CRTC and seven overlay
struct rcar_du_plane *plane = &planes->planes[i]; * planes.
*/
plane->group = rgrp; num_crtcs = min(rcdu->num_crtcs - 2 * rgrp->index, 2U);
plane->hwindex = -1; num_planes = num_crtcs + 7;
plane->alpha = 255;
plane->colorkey = RCAR_DU_COLORKEY_NONE;
plane->zpos = 0;
}
return 0;
}
int rcar_du_planes_register(struct rcar_du_group *rgrp)
{
struct rcar_du_planes *planes = &rgrp->planes;
struct rcar_du_device *rcdu = rgrp->dev;
unsigned int crtcs;
unsigned int i;
int ret;
crtcs = ((1 << rcdu->num_crtcs) - 1) & (3 << (2 * rgrp->index)); crtcs = ((1 << rcdu->num_crtcs) - 1) & (3 << (2 * rgrp->index));
for (i = 0; i < RCAR_DU_NUM_KMS_PLANES; ++i) { for (i = 0; i < num_planes; ++i) {
struct rcar_du_kms_plane *plane; enum drm_plane_type type = i < num_crtcs
? DRM_PLANE_TYPE_PRIMARY
plane = devm_kzalloc(rcdu->dev, sizeof(*plane), GFP_KERNEL); : DRM_PLANE_TYPE_OVERLAY;
if (plane == NULL) struct rcar_du_plane *plane = &planes->planes[i];
return -ENOMEM;
plane->hwplane = &planes->planes[i + 2]; plane->group = rgrp;
plane->hwplane->zpos = 1;
ret = drm_plane_init(rcdu->ddev, &plane->plane, crtcs, ret = drm_universal_plane_init(rcdu->ddev, &plane->plane, crtcs,
&rcar_du_plane_funcs, formats, &rcar_du_plane_funcs, formats,
ARRAY_SIZE(formats), false); ARRAY_SIZE(formats), type);
if (ret < 0) if (ret < 0)
return ret; return ret;
drm_plane_helper_add(&plane->plane,
&rcar_du_plane_helper_funcs);
if (type == DRM_PLANE_TYPE_PRIMARY)
continue;
drm_object_attach_property(&plane->plane.base, drm_object_attach_property(&plane->plane.base,
planes->alpha, 255); planes->alpha, 255);
drm_object_attach_property(&plane->plane.base, drm_object_attach_property(&plane->plane.base,
......
...@@ -14,68 +14,57 @@ ...@@ -14,68 +14,57 @@
#ifndef __RCAR_DU_PLANE_H__ #ifndef __RCAR_DU_PLANE_H__
#define __RCAR_DU_PLANE_H__ #define __RCAR_DU_PLANE_H__
#include <linux/mutex.h>
#include <drm/drmP.h> #include <drm/drmP.h>
#include <drm/drm_crtc.h> #include <drm/drm_crtc.h>
struct rcar_du_format_info; struct rcar_du_format_info;
struct rcar_du_group; struct rcar_du_group;
/* The RCAR DU has 8 hardware planes, shared between KMS planes and CRTCs. As /* The RCAR DU has 8 hardware planes, shared between primary and overlay planes.
* using KMS planes requires at least one of the CRTCs being enabled, no more * As using overlay planes requires at least one of the CRTCs being enabled, no
* than 7 KMS planes can be available. We thus create 7 KMS planes and * more than 7 overlay planes can be available. We thus create 1 primary plane
* 9 software planes (one for each KMS planes and one for each CRTC). * per CRTC and 7 overlay planes, for a total of up to 9 KMS planes.
*/ */
#define RCAR_DU_NUM_KMS_PLANES 9
#define RCAR_DU_NUM_KMS_PLANES 7
#define RCAR_DU_NUM_HW_PLANES 8 #define RCAR_DU_NUM_HW_PLANES 8
#define RCAR_DU_NUM_SW_PLANES 9
struct rcar_du_plane { struct rcar_du_plane {
struct drm_plane plane;
struct rcar_du_group *group; struct rcar_du_group *group;
struct drm_crtc *crtc;
bool enabled;
int hwindex; /* 0-based, -1 means unused */
unsigned int alpha;
unsigned int colorkey;
unsigned int zpos;
const struct rcar_du_format_info *format;
unsigned long dma[2];
unsigned int pitch;
unsigned int width;
unsigned int height;
unsigned int src_x;
unsigned int src_y;
unsigned int dst_x;
unsigned int dst_y;
}; };
static inline struct rcar_du_plane *to_rcar_plane(struct drm_plane *plane)
{
return container_of(plane, struct rcar_du_plane, plane);
}
struct rcar_du_planes { struct rcar_du_planes {
struct rcar_du_plane planes[RCAR_DU_NUM_SW_PLANES]; struct rcar_du_plane planes[RCAR_DU_NUM_KMS_PLANES];
unsigned int free;
struct mutex lock;
struct drm_property *alpha; struct drm_property *alpha;
struct drm_property *colorkey; struct drm_property *colorkey;
struct drm_property *zpos; struct drm_property *zpos;
}; };
struct rcar_du_plane_state {
struct drm_plane_state state;
const struct rcar_du_format_info *format;
int hwindex; /* 0-based, -1 means unused */
unsigned int alpha;
unsigned int colorkey;
unsigned int zpos;
};
static inline struct rcar_du_plane_state *
to_rcar_du_plane_state(struct drm_plane_state *state)
{
return container_of(state, struct rcar_du_plane_state, state);
}
int rcar_du_planes_init(struct rcar_du_group *rgrp); int rcar_du_planes_init(struct rcar_du_group *rgrp);
int rcar_du_planes_register(struct rcar_du_group *rgrp);
void rcar_du_plane_setup(struct rcar_du_plane *plane); void rcar_du_plane_setup(struct rcar_du_plane *plane);
void rcar_du_plane_update_base(struct rcar_du_plane *plane);
void rcar_du_plane_compute_base(struct rcar_du_plane *plane,
struct drm_framebuffer *fb);
int rcar_du_plane_reserve(struct rcar_du_plane *plane,
const struct rcar_du_format_info *format);
void rcar_du_plane_release(struct rcar_du_plane *plane);
#endif /* __RCAR_DU_PLANE_H__ */ #endif /* __RCAR_DU_PLANE_H__ */
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
*/ */
#include <drm/drmP.h> #include <drm/drmP.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc.h> #include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h> #include <drm/drm_crtc_helper.h>
...@@ -43,10 +44,13 @@ rcar_du_vga_connector_detect(struct drm_connector *connector, bool force) ...@@ -43,10 +44,13 @@ rcar_du_vga_connector_detect(struct drm_connector *connector, bool force)
} }
static const struct drm_connector_funcs connector_funcs = { static const struct drm_connector_funcs connector_funcs = {
.dpms = drm_helper_connector_dpms, .dpms = drm_atomic_helper_connector_dpms,
.reset = drm_atomic_helper_connector_reset,
.detect = rcar_du_vga_connector_detect, .detect = rcar_du_vga_connector_detect,
.fill_modes = drm_helper_probe_single_connector_modes, .fill_modes = drm_helper_probe_single_connector_modes,
.destroy = rcar_du_vga_connector_destroy, .destroy = rcar_du_vga_connector_destroy,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
}; };
int rcar_du_vga_connector_init(struct rcar_du_device *rcdu, int rcar_du_vga_connector_init(struct rcar_du_device *rcdu,
...@@ -76,7 +80,7 @@ int rcar_du_vga_connector_init(struct rcar_du_device *rcdu, ...@@ -76,7 +80,7 @@ int rcar_du_vga_connector_init(struct rcar_du_device *rcdu,
if (ret < 0) if (ret < 0)
return ret; return ret;
drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF); connector->dpms = DRM_MODE_DPMS_OFF;
drm_object_property_set_value(&connector->base, drm_object_property_set_value(&connector->base,
rcdu->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF); rcdu->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF);
...@@ -84,7 +88,6 @@ int rcar_du_vga_connector_init(struct rcar_du_device *rcdu, ...@@ -84,7 +88,6 @@ int rcar_du_vga_connector_init(struct rcar_du_device *rcdu,
if (ret < 0) if (ret < 0)
return ret; return ret;
connector->encoder = encoder;
rcon->encoder = renc; rcon->encoder = renc;
return 0; return 0;
......
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