Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+185 −215
Original line number Diff line number Diff line
@@ -15,6 +15,8 @@
#include <linux/mutex.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_helper.h>
#include <drm/drm_fb_cma_helper.h>
@@ -99,9 +101,13 @@ static void rcar_du_crtc_put(struct rcar_du_crtc *rcrtc)
	clk_disable_unprepare(rcrtc->clock);
}

/* -----------------------------------------------------------------------------
 * Hardware Setup
 */

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 clk;
	u32 value;
@@ -187,9 +193,19 @@ void rcar_du_crtc_route_output(struct drm_crtc *crtc,
		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];
	unsigned int num_planes = 0;
	unsigned int prio = 0;
@@ -201,29 +217,30 @@ void rcar_du_crtc_update_planes(struct drm_crtc *crtc)
		struct rcar_du_plane *plane = &rcrtc->group->planes.planes[i];
		unsigned int j;

		if (plane->crtc != &rcrtc->crtc || !plane->enabled)
		if (plane->plane.state->crtc != &rcrtc->crtc)
			continue;

		/* Insert the plane in the sorted planes array. */
		for (j = num_planes++; j > 0; --j) {
			if (planes[j-1]->zpos <= plane->zpos)
			if (plane_zpos(planes[j-1]) <= plane_zpos(plane))
				break;
			planes[j] = planes[j-1];
		}

		planes[j] = plane;
		prio += plane->format->planes * 4;
		prio += plane_format(plane)->planes * 4;
	}

	for (i = 0; i < num_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;
		dspr |= (index + 1) << prio;
		dptsr |= DPTSR_PnDK(index) |  DPTSR_PnTS(index);

		if (plane->format->planes == 2) {
		if (plane_format(plane)->planes == 2) {
			index = (index + 1) % 8;

			prio -= 4;
@@ -236,8 +253,6 @@ void rcar_du_crtc_update_planes(struct drm_crtc *crtc)
	 * with superposition controller 2.
	 */
	if (rcrtc->index % 2) {
		u32 value = rcar_du_group_read(rcrtc->group, DPTSR);

		/* The DPTSR register is updated when the display controller is
		 * stopped. We thus need to restart the DU. Once again, sorry
		 * 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)
		 * split, or through a module parameter). Flicker would then
		 * 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);
			if (rcrtc->group->used_crtcs)
				rcar_du_group_restart(rcrtc->group);
		}
		mutex_unlock(&rcrtc->group->lock);
	}

	rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? DS2PR : DS1PR,
			    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)
{
	struct drm_crtc *crtc = &rcrtc->crtc;
	bool interlaced;
	unsigned int i;

	if (rcrtc->started)
		return;

	if (WARN_ON(rcrtc->plane->format == NULL))
		return;

	/* Set display off and background to black */
	rcar_du_crtc_write(rcrtc, DOOR, DOOR_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)
	rcar_du_crtc_set_display_timing(rcrtc);
	rcar_du_group_set_routing(rcrtc->group);

	mutex_lock(&rcrtc->group->planes.lock);
	rcrtc->plane->enabled = true;
	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);
	}
	/* Start with all planes disabled. */
	rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? DS2PR : DS1PR, 0);

	/* Select master sync mode. This enables display operation in master
	 * 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)

	rcar_du_group_start_stop(rcrtc->group, true);

	/* Turn vertical blanking interrupt reporting back on. */
	drm_crtc_vblank_on(crtc);

	rcrtc->started = true;
}

@@ -312,10 +393,12 @@ static void rcar_du_crtc_stop(struct rcar_du_crtc *rcrtc)
	if (!rcrtc->started)
		return;

	mutex_lock(&rcrtc->group->planes.lock);
	rcrtc->plane->enabled = false;
	rcar_du_crtc_update_planes(crtc);
	mutex_unlock(&rcrtc->group->planes.lock);
	/* Disable vertical blanking interrupt reporting. We first need to wait
	 * for page flip completion before stopping the CRTC as userspace
	 * expects page flips to eventually complete.
	 */
	rcar_du_crtc_wait_page_flip(rcrtc);
	drm_crtc_vblank_off(crtc);

	/* Select switch sync mode. This stops display operation and configures
	 * the HSYNC and VSYNC signals as inputs.
@@ -335,196 +418,111 @@ void rcar_du_crtc_suspend(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;

	rcar_du_crtc_get(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)
		mode = DRM_MODE_DPMS_OFF;
	/* Commit the planes state. */
	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)
		return;
		if (plane->plane.state->crtc != &rcrtc->crtc)
			continue;

	if (mode == DRM_MODE_DPMS_ON) {
		rcar_du_crtc_get(rcrtc);
		rcar_du_crtc_start(rcrtc);
	} else {
		rcar_du_crtc_stop(rcrtc);
		rcar_du_crtc_put(rcrtc);
		rcar_du_plane_setup(plane);
	}

	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,
				    struct drm_display_mode *adjusted_mode)
{
	/* TODO Fixup modes */
	return true;
}
/* -----------------------------------------------------------------------------
 * CRTC Functions
 */

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);

	/* We need to access the hardware during mode set, acquire a reference
	 * to the CRTC.
	 */
	rcar_du_crtc_get(rcrtc);
	if (rcrtc->enabled)
		return;

	/* Stop the CRTC and release the plane. Force the DPMS mode to off as a
	 * result.
	 */
	rcar_du_crtc_stop(rcrtc);
	rcar_du_plane_release(rcrtc->plane);
	rcar_du_crtc_get(rcrtc);
	rcar_du_crtc_start(rcrtc);

	rcrtc->dpms = DRM_MODE_DPMS_OFF;
	rcrtc->enabled = true;
}

static int rcar_du_crtc_mode_set(struct drm_crtc *crtc,
				 struct drm_display_mode *mode,
				 struct drm_display_mode *adjusted_mode,
				 int x, int y,
				 struct drm_framebuffer *old_fb)
static void rcar_du_crtc_disable(struct drm_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 (ret < 0)
		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;
	if (!rcrtc->enabled)
		return;

	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;

	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);

	/* 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;
	/* TODO Fixup modes */
	return true;
}

static int rcar_du_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
				      struct drm_framebuffer *old_fb)
static void rcar_du_crtc_atomic_begin(struct drm_crtc *crtc)
{
	struct drm_pending_vblank_event *event = crtc->state->event;
	struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
	struct drm_device *dev = rcrtc->crtc.dev;
	unsigned long flags;

	rcrtc->plane->src_x = x;
	rcrtc->plane->src_y = y;
	if (event) {
		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);

	rcar_du_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
	rcar_du_plane_release(rcrtc->plane);
	rcar_du_crtc_update_planes(rcrtc);
}

static const struct drm_crtc_helper_funcs crtc_helper_funcs = {
	.dpms = rcar_du_crtc_dpms,
	.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,
	.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,
				   struct drm_file *file)
{
	struct drm_pending_vblank_event *event;
	struct drm_device *dev = rcrtc->crtc.dev;
	unsigned long flags;
static const struct drm_crtc_funcs crtc_funcs = {
	.reset = drm_atomic_helper_crtc_reset,
	.destroy = drm_crtc_cleanup,
	.set_config = drm_atomic_helper_set_config,
	.page_flip = drm_atomic_helper_page_flip,
	.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.
/* -----------------------------------------------------------------------------
 * Interrupt Handling
 */
	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);
}

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;
}

static int rcar_du_crtc_page_flip(struct drm_crtc *crtc,
				  struct drm_framebuffer *fb,
				  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,
};
/* -----------------------------------------------------------------------------
 * Initialization
 */

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;
	}

	init_waitqueue_head(&rcrtc->flip_wait);

	rcrtc->group = rgrp;
	rcrtc->mmio_offset = mmio_offsets[index];
	rcrtc->index = index;
	rcrtc->dpms = DRM_MODE_DPMS_OFF;
	rcrtc->plane = &rgrp->planes.planes[index % 2];

	rcrtc->plane->crtc = crtc;
	rcrtc->enabled = false;

	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)
		return ret;

	drm_crtc_helper_add(crtc, &crtc_helper_funcs);

	/* Start with vertical blanking interrupt reporting disabled. */
	drm_crtc_vblank_off(crtc);

	/* Register the interrupt handler. */
	if (rcar_du_has(rcdu, RCAR_DU_FEATURE_CRTC_IRQ_CLOCK)) {
		irq = platform_get_irq(pdev, index);
+4 −4
Original line number Diff line number Diff line
@@ -15,12 +15,12 @@
#define __RCAR_DU_CRTC_H__

#include <linux/mutex.h>
#include <linux/wait.h>

#include <drm/drmP.h>
#include <drm/drm_crtc.h>

struct rcar_du_group;
struct rcar_du_plane;

struct rcar_du_crtc {
	struct drm_crtc crtc;
@@ -32,11 +32,12 @@ struct rcar_du_crtc {
	bool started;

	struct drm_pending_vblank_event *event;
	wait_queue_head_t flip_wait;

	unsigned int outputs;
	int dpms;
	bool enabled;

	struct rcar_du_group *group;
	struct rcar_du_plane *plane;
};

#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);

void rcar_du_crtc_route_output(struct drm_crtc *crtc,
			       enum rcar_du_output output);
void rcar_du_crtc_update_planes(struct drm_crtc *crtc);

#endif /* __RCAR_DU_CRTC_H__ */
+11 −6
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/slab.h>
#include <linux/wait.h>

#include <drm/drmP.h>
#include <drm/drm_crtc_helper.h>
@@ -163,6 +164,8 @@ static int rcar_du_load(struct drm_device *dev, unsigned long flags)
		return -ENOMEM;
	}

	init_waitqueue_head(&rcdu->commit.wait);

	rcdu->dev = &pdev->dev;
	rcdu->info = np ? of_match_device(rcar_du_of_table, rcdu->dev)->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)
	if (IS_ERR(rcdu->mmio))
		return PTR_ERR(rcdu->mmio);

	/* DRM/KMS objects */
	ret = rcar_du_modeset_init(rcdu);
	/* Initialize vertical blanking interrupts handling. Start with vblank
	 * disabled for all CRTCs.
	 */
	ret = drm_vblank_init(dev, (1 << rcdu->info->num_crtcs) - 1);
	if (ret < 0) {
		dev_err(&pdev->dev, "failed to initialize DRM/KMS\n");
		dev_err(&pdev->dev, "failed to initialize vblank\n");
		goto done;
	}

	/* vblank handling */
	ret = drm_vblank_init(dev, (1 << rcdu->num_crtcs) - 1);
	/* DRM/KMS objects */
	ret = rcar_du_modeset_init(rcdu);
	if (ret < 0) {
		dev_err(&pdev->dev, "failed to initialize vblank\n");
		dev_err(&pdev->dev, "failed to initialize DRM/KMS\n");
		goto done;
	}

+13 −3
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@
#define __RCAR_DU_DRV_H__

#include <linux/kernel.h>
#include <linux/wait.h>

#include "rcar_du_crtc.h"
#include "rcar_du_group.h"
@@ -64,6 +65,10 @@ struct rcar_du_device_info {
	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 device *dev;
	const struct rcar_du_device_info *info;
@@ -73,13 +78,18 @@ struct rcar_du_device {
	struct drm_device *ddev;
	struct drm_fbdev_cma *fbdev;

	struct rcar_du_crtc crtcs[3];
	struct rcar_du_crtc crtcs[RCAR_DU_MAX_CRTCS];
	unsigned int num_crtcs;

	struct rcar_du_group groups[2];
	struct rcar_du_group groups[RCAR_DU_MAX_GROUPS];

	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,
+23 −48
Original line number Diff line number Diff line
@@ -42,46 +42,40 @@ rcar_du_connector_best_encoder(struct drm_connector *connector)
 * 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);

	if (mode != DRM_MODE_DPMS_ON)
		mode = DRM_MODE_DPMS_OFF;
	if (renc->lvds)
		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)
		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,
				       const struct drm_display_mode *mode,
				       struct drm_display_mode *adjusted_mode)
static int rcar_du_encoder_atomic_check(struct drm_encoder *encoder,
					struct drm_crtc_state *crtc_state,
					struct drm_connector_state *conn_state)
{
	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;
	struct drm_connector *connector = conn_state->connector;
	struct drm_device *dev = encoder->dev;
	struct drm_connector *connector;
	bool found = false;

	/* DAC encoders have currently no restriction on the mode. */
	if (encoder->encoder_type == DRM_MODE_ENCODER_DAC)
		return true;

	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;
	}
		return 0;

	if (list_empty(&connector->modes)) {
		dev_dbg(dev->dev, "mode_fixup: empty modes list\n");
		return false;
		dev_dbg(dev->dev, "encoder: empty modes list\n");
		return -EINVAL;
	}

	panel_mode = list_first_entry(&connector->modes,
@@ -90,7 +84,7 @@ static bool rcar_du_encoder_mode_fixup(struct drm_encoder *encoder,
	/* We're not allowed to modify the resolution. */
	if (mode->hdisplay != panel_mode->hdisplay ||
	    mode->vdisplay != panel_mode->vdisplay)
		return false;
		return -EINVAL;

	/* The flat panel mode is fixed, just copy it to the adjusted mode. */
	drm_mode_copy(adjusted_mode, panel_mode);
@@ -102,25 +96,7 @@ static bool rcar_du_encoder_mode_fixup(struct drm_encoder *encoder,
		adjusted_mode->clock = clamp(adjusted_mode->clock,
					     30000, 150000);

	return true;
}

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);
	return 0;
}

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 = {
	.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,
	.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 = {
Loading