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

Commit 67a0375f authored by Dave Airlie's avatar Dave Airlie
Browse files

Merge tag 'omapdrm-4.1' of git://git.kernel.org/pub/scm/linux/kernel/git/tomba/linux into drm-next

omapdrm changes for 4.1

* universal plane support
* refactoring to prepare work atomic modesetting work
* a lot of small fixes

* tag 'omapdrm-4.1' of git://git.kernel.org/pub/scm/linux/kernel/git/tomba/linux: (36 commits)
  drm/omap: tiler: add hibernation callback
  drm/omap: add hibernation callbacks
  drm/omap: keep ref to old_fb
  drm/omap: fix race conditon in DMM
  drm/omap: fix race condition with dev->obj_list
  drm/omap: do not use BUG_ON(!spin_is_locked(x))
  drm/omap: only ignore DIGIT SYNC LOST for TV output
  drm/omap: fix race with error_irq
  drm/omap: use DRM_ERROR_RATELIMITED() for error irqs
  drm/omap: stop connector polling during suspend
  drm/omap: remove dummy PM functions
  drm/omap: tiler: fix race condition with engine->async
  drm/omap: fix plane's channel selection
  drm/omap: fix TILER on OMAP5
  drm/omap: handle incompatible buffer stride and pixel size
  drm/omap: fix error handling in omap_framebuffer_create()
  drm/omap: fix operation without fbdev
  drm/omap: add a comment why locking is missing
  drm/omap: add pin refcounting to omap_framebuffer
  drm/omap: clear omap_obj->paddr in omap_gem_put_paddr()
  ...
parents 4d0982c6 1d601da2
Loading
Loading
Loading
Loading
+0 −12
Original line number Diff line number Diff line
@@ -271,18 +271,6 @@ static const struct drm_connector_helper_funcs omap_connector_helper_funcs = {
	.best_encoder = omap_connector_attached_encoder,
};

/* flush an area of the framebuffer (in case of manual update display that
 * is not automatically flushed)
 */
void omap_connector_flush(struct drm_connector *connector,
		int x, int y, int w, int h)
{
	struct omap_connector *omap_connector = to_omap_connector(connector);

	/* TODO: enable when supported in dss */
	VERB("%s: %d,%d, %dx%d", omap_connector->dssdev->name, x, y, w, h);
}

/* initialize connector */
struct drm_connector *omap_connector_init(struct drm_device *dev,
		int connector_type, struct omap_dss_device *dssdev,
+320 −302
Original line number Diff line number Diff line
@@ -28,7 +28,6 @@

struct omap_crtc {
	struct drm_crtc base;
	struct drm_plane *plane;

	const char *name;
	int pipe;
@@ -46,7 +45,6 @@ struct omap_crtc {

	struct omap_video_timings timings;
	bool enabled;
	bool full_update;

	struct omap_drm_apply apply;

@@ -74,8 +72,14 @@ struct omap_crtc {
	 * XXX maybe fold into apply_work??
	 */
	struct work_struct page_flip_work;

	bool ignore_digit_sync_lost;
};

/* -----------------------------------------------------------------------------
 * Helper Functions
 */

uint32_t pipe2vbl(struct drm_crtc *crtc)
{
	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
@@ -83,6 +87,22 @@ uint32_t pipe2vbl(struct drm_crtc *crtc)
	return dispc_mgr_get_vsync_irq(omap_crtc->channel);
}

const struct omap_video_timings *omap_crtc_timings(struct drm_crtc *crtc)
{
	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
	return &omap_crtc->timings;
}

enum omap_channel omap_crtc_channel(struct drm_crtc *crtc)
{
	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
	return omap_crtc->channel;
}

/* -----------------------------------------------------------------------------
 * DSS Manager Functions
 */

/*
 * Manager-ops, callbacks from output when they need to configure
 * the upstream part of the video pipe.
@@ -122,7 +142,63 @@ static void omap_crtc_start_update(struct omap_overlay_manager *mgr)
{
}

static void set_enabled(struct drm_crtc *crtc, bool enable);
/* Called only from CRTC pre_apply and suspend/resume handlers. */
static void omap_crtc_set_enabled(struct drm_crtc *crtc, bool enable)
{
	struct drm_device *dev = crtc->dev;
	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
	enum omap_channel channel = omap_crtc->channel;
	struct omap_irq_wait *wait;
	u32 framedone_irq, vsync_irq;
	int ret;

	if (dispc_mgr_is_enabled(channel) == enable)
		return;

	if (omap_crtc->channel == OMAP_DSS_CHANNEL_DIGIT) {
		/*
		 * Digit output produces some sync lost interrupts during the
		 * first frame when enabling, so we need to ignore those.
		 */
		omap_crtc->ignore_digit_sync_lost = true;
	}

	framedone_irq = dispc_mgr_get_framedone_irq(channel);
	vsync_irq = dispc_mgr_get_vsync_irq(channel);

	if (enable) {
		wait = omap_irq_wait_init(dev, vsync_irq, 1);
	} else {
		/*
		 * When we disable the digit output, we need to wait for
		 * FRAMEDONE to know that DISPC has finished with the output.
		 *
		 * OMAP2/3 does not have FRAMEDONE irq for digit output, and in
		 * that case we need to use vsync interrupt, and wait for both
		 * even and odd frames.
		 */

		if (framedone_irq)
			wait = omap_irq_wait_init(dev, framedone_irq, 1);
		else
			wait = omap_irq_wait_init(dev, vsync_irq, 2);
	}

	dispc_mgr_enable(channel, enable);

	ret = omap_irq_wait(dev, wait, msecs_to_jiffies(100));
	if (ret) {
		dev_err(dev->dev, "%s: timeout waiting for %s\n",
				omap_crtc->name, enable ? "enable" : "disable");
	}

	if (omap_crtc->channel == OMAP_DSS_CHANNEL_DIGIT) {
		omap_crtc->ignore_digit_sync_lost = false;
		/* make sure the irq handler sees the value above */
		mb();
	}
}


static int omap_crtc_enable(struct omap_overlay_manager *mgr)
{
@@ -131,7 +207,7 @@ static int omap_crtc_enable(struct omap_overlay_manager *mgr)
	dispc_mgr_setup(omap_crtc->channel, &omap_crtc->info);
	dispc_mgr_set_timings(omap_crtc->channel,
			&omap_crtc->timings);
	set_enabled(&omap_crtc->base, true);
	omap_crtc_set_enabled(&omap_crtc->base, true);

	return 0;
}
@@ -140,7 +216,7 @@ static void omap_crtc_disable(struct omap_overlay_manager *mgr)
{
	struct omap_crtc *omap_crtc = omap_crtcs[mgr->id];

	set_enabled(&omap_crtc->base, false);
	omap_crtc_set_enabled(&omap_crtc->base, false);
}

static void omap_crtc_set_timings(struct omap_overlay_manager *mgr,
@@ -149,7 +225,6 @@ static void omap_crtc_set_timings(struct omap_overlay_manager *mgr,
	struct omap_crtc *omap_crtc = omap_crtcs[mgr->id];
	DBG("%s", omap_crtc->name);
	omap_crtc->timings = *timings;
	omap_crtc->full_update = true;
}

static void omap_crtc_set_lcd_config(struct omap_overlay_manager *mgr,
@@ -185,8 +260,190 @@ static const struct dss_mgr_ops mgr_ops = {
	.unregister_framedone_handler = omap_crtc_unregister_framedone_handler,
};

/* -----------------------------------------------------------------------------
 * Apply Logic
 */

static void omap_crtc_error_irq(struct omap_drm_irq *irq, uint32_t irqstatus)
{
	struct omap_crtc *omap_crtc =
			container_of(irq, struct omap_crtc, error_irq);

	if (omap_crtc->ignore_digit_sync_lost) {
		irqstatus &= ~DISPC_IRQ_SYNC_LOST_DIGIT;
		if (!irqstatus)
			return;
	}

	DRM_ERROR_RATELIMITED("%s: errors: %08x\n", omap_crtc->name, irqstatus);
}

static void omap_crtc_apply_irq(struct omap_drm_irq *irq, uint32_t irqstatus)
{
	struct omap_crtc *omap_crtc =
			container_of(irq, struct omap_crtc, apply_irq);
	struct drm_crtc *crtc = &omap_crtc->base;

	if (!dispc_mgr_go_busy(omap_crtc->channel)) {
		struct omap_drm_private *priv =
				crtc->dev->dev_private;
		DBG("%s: apply done", omap_crtc->name);
		__omap_irq_unregister(crtc->dev, &omap_crtc->apply_irq);
		queue_work(priv->wq, &omap_crtc->apply_work);
	}
}

static void apply_worker(struct work_struct *work)
{
	struct omap_crtc *omap_crtc =
			container_of(work, struct omap_crtc, apply_work);
	struct drm_crtc *crtc = &omap_crtc->base;
	struct drm_device *dev = crtc->dev;
	struct omap_drm_apply *apply, *n;
	bool need_apply;

	/*
	 * Synchronize everything on mode_config.mutex, to keep
	 * the callbacks and list modification all serialized
	 * with respect to modesetting ioctls from userspace.
	 */
	drm_modeset_lock(&crtc->mutex, NULL);
	dispc_runtime_get();

	/*
	 * If we are still pending a previous update, wait.. when the
	 * pending update completes, we get kicked again.
	 */
	if (omap_crtc->apply_irq.registered)
		goto out;

	/* finish up previous apply's: */
	list_for_each_entry_safe(apply, n,
			&omap_crtc->pending_applies, pending_node) {
		apply->post_apply(apply);
		list_del(&apply->pending_node);
	}

	need_apply = !list_empty(&omap_crtc->queued_applies);

	/* then handle the next round of of queued apply's: */
	list_for_each_entry_safe(apply, n,
			&omap_crtc->queued_applies, queued_node) {
		apply->pre_apply(apply);
		list_del(&apply->queued_node);
		apply->queued = false;
		list_add_tail(&apply->pending_node,
				&omap_crtc->pending_applies);
	}

	if (need_apply) {
		enum omap_channel channel = omap_crtc->channel;

		DBG("%s: GO", omap_crtc->name);

		if (dispc_mgr_is_enabled(channel)) {
			dispc_mgr_go(channel);
			omap_irq_register(dev, &omap_crtc->apply_irq);
		} else {
			struct omap_drm_private *priv = dev->dev_private;
			queue_work(priv->wq, &omap_crtc->apply_work);
		}
	}

out:
	dispc_runtime_put();
	drm_modeset_unlock(&crtc->mutex);
}

int omap_crtc_apply(struct drm_crtc *crtc,
		struct omap_drm_apply *apply)
{
	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);

	WARN_ON(!drm_modeset_is_locked(&crtc->mutex));

	/* no need to queue it again if it is already queued: */
	if (apply->queued)
		return 0;

	apply->queued = true;
	list_add_tail(&apply->queued_node, &omap_crtc->queued_applies);

	/*
 * CRTC funcs:
	 * If there are no currently pending updates, then go ahead and
	 * kick the worker immediately, otherwise it will run again when
	 * the current update finishes.
	 */
	if (list_empty(&omap_crtc->pending_applies)) {
		struct omap_drm_private *priv = crtc->dev->dev_private;
		queue_work(priv->wq, &omap_crtc->apply_work);
	}

	return 0;
}

static void omap_crtc_pre_apply(struct omap_drm_apply *apply)
{
	struct omap_crtc *omap_crtc =
			container_of(apply, struct omap_crtc, apply);
	struct drm_crtc *crtc = &omap_crtc->base;
	struct omap_drm_private *priv = crtc->dev->dev_private;
	struct drm_encoder *encoder = NULL;
	unsigned int i;

	DBG("%s: enabled=%d", omap_crtc->name, omap_crtc->enabled);

	for (i = 0; i < priv->num_encoders; i++) {
		if (priv->encoders[i]->crtc == crtc) {
			encoder = priv->encoders[i];
			break;
		}
	}

	if (omap_crtc->current_encoder && encoder != omap_crtc->current_encoder)
		omap_encoder_set_enabled(omap_crtc->current_encoder, false);

	omap_crtc->current_encoder = encoder;

	if (!omap_crtc->enabled) {
		if (encoder)
			omap_encoder_set_enabled(encoder, false);
	} else {
		if (encoder) {
			omap_encoder_set_enabled(encoder, false);
			omap_encoder_update(encoder, omap_crtc->mgr,
					&omap_crtc->timings);
			omap_encoder_set_enabled(encoder, true);
		}
	}
}

static void omap_crtc_post_apply(struct omap_drm_apply *apply)
{
	/* nothing needed for post-apply */
}

void omap_crtc_flush(struct drm_crtc *crtc)
{
	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
	int loops = 0;

	while (!list_empty(&omap_crtc->pending_applies) ||
		!list_empty(&omap_crtc->queued_applies) ||
		omap_crtc->event || omap_crtc->old_fb) {

		if (++loops > 10) {
			dev_err(crtc->dev->dev,
				"omap_crtc_flush() timeout\n");
			break;
		}

		schedule_timeout_uninterruptible(msecs_to_jiffies(20));
	}
}

/* -----------------------------------------------------------------------------
 * CRTC Functions
 */

static void omap_crtc_destroy(struct drm_crtc *crtc)
@@ -214,17 +471,13 @@ static void omap_crtc_dpms(struct drm_crtc *crtc, int mode)

	if (enabled != omap_crtc->enabled) {
		omap_crtc->enabled = enabled;
		omap_crtc->full_update = true;
		omap_crtc_apply(crtc, &omap_crtc->apply);

		/* also enable our private plane: */
		WARN_ON(omap_plane_dpms(omap_crtc->plane, mode));

		/* and any attached overlay planes: */
		/* Enable/disable all planes associated with the CRTC. */
		for (i = 0; i < priv->num_planes; i++) {
			struct drm_plane *plane = priv->planes[i];
			if (plane->crtc == crtc)
				WARN_ON(omap_plane_dpms(plane, mode));
				WARN_ON(omap_plane_set_enable(plane, enabled));
		}
	}
}
@@ -256,12 +509,16 @@ static int omap_crtc_mode_set(struct drm_crtc *crtc,
			mode->type, mode->flags);

	copy_timings_drm_to_omap(&omap_crtc->timings, mode);
	omap_crtc->full_update = true;

	return omap_plane_mode_set(omap_crtc->plane, crtc, crtc->primary->fb,
	/*
	 * The primary plane CRTC can be reset if the plane is disabled directly
	 * through the universal plane API. Set it again here.
	 */
	crtc->primary->crtc = crtc;

	return omap_plane_mode_set(crtc->primary, crtc, crtc->primary->fb,
				   0, 0, mode->hdisplay, mode->vdisplay,
			x << 16, y << 16,
			mode->hdisplay << 16, mode->vdisplay << 16,
				   x, y, mode->hdisplay, mode->vdisplay,
				   NULL, NULL);
}

@@ -282,14 +539,12 @@ static void omap_crtc_commit(struct drm_crtc *crtc)
static int omap_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
		struct drm_framebuffer *old_fb)
{
	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
	struct drm_plane *plane = omap_crtc->plane;
	struct drm_plane *plane = crtc->primary;
	struct drm_display_mode *mode = &crtc->mode;

	return omap_plane_mode_set(plane, crtc, crtc->primary->fb,
				   0, 0, mode->hdisplay, mode->vdisplay,
			x << 16, y << 16,
			mode->hdisplay << 16, mode->vdisplay << 16,
				   x, y, mode->hdisplay, mode->vdisplay,
				   NULL, NULL);
}

@@ -299,6 +554,7 @@ static void vblank_cb(void *arg)
	struct drm_device *dev = crtc->dev;
	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
	unsigned long flags;
	struct drm_framebuffer *fb;

	spin_lock_irqsave(&dev->event_lock, flags);

@@ -306,10 +562,15 @@ static void vblank_cb(void *arg)
	if (omap_crtc->event)
		drm_send_vblank_event(dev, omap_crtc->pipe, omap_crtc->event);

	fb = omap_crtc->old_fb;

	omap_crtc->event = NULL;
	omap_crtc->old_fb = NULL;

	spin_unlock_irqrestore(&dev->event_lock, flags);

	if (fb)
		drm_framebuffer_unreference(fb);
}

static void page_flip_worker(struct work_struct *work)
@@ -321,10 +582,9 @@ static void page_flip_worker(struct work_struct *work)
	struct drm_gem_object *bo;

	drm_modeset_lock(&crtc->mutex, NULL);
	omap_plane_mode_set(omap_crtc->plane, crtc, crtc->primary->fb,
	omap_plane_mode_set(crtc->primary, crtc, crtc->primary->fb,
			    0, 0, mode->hdisplay, mode->vdisplay,
			crtc->x << 16, crtc->y << 16,
			mode->hdisplay << 16, mode->vdisplay << 16,
			    crtc->x, crtc->y, mode->hdisplay, mode->vdisplay,
			    vblank_cb, crtc);
	drm_modeset_unlock(&crtc->mutex);

@@ -361,11 +621,12 @@ static int omap_crtc_page_flip_locked(struct drm_crtc *crtc,
	if (omap_crtc->old_fb) {
		spin_unlock_irqrestore(&dev->event_lock, flags);
		dev_err(dev->dev, "already a pending flip\n");
		return -EINVAL;
		return -EBUSY;
	}

	omap_crtc->event = event;
	omap_crtc->old_fb = primary->fb = fb;
	drm_framebuffer_reference(omap_crtc->old_fb);

	spin_unlock_irqrestore(&dev->event_lock, flags);

@@ -385,7 +646,6 @@ static int omap_crtc_page_flip_locked(struct drm_crtc *crtc,
static int omap_crtc_set_property(struct drm_crtc *crtc,
		struct drm_property *property, uint64_t val)
{
	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
	struct omap_drm_private *priv = crtc->dev->dev_private;

	if (property == priv->rotation_prop) {
@@ -393,7 +653,7 @@ static int omap_crtc_set_property(struct drm_crtc *crtc,
				!!(val & ((1LL << DRM_ROTATE_90) | (1LL << DRM_ROTATE_270)));
	}

	return omap_plane_set_property(omap_crtc->plane, property, val);
	return omap_plane_set_property(crtc->primary, property, val);
}

static const struct drm_crtc_funcs omap_crtc_funcs = {
@@ -412,250 +672,9 @@ static const struct drm_crtc_helper_funcs omap_crtc_helper_funcs = {
	.mode_set_base = omap_crtc_mode_set_base,
};

const struct omap_video_timings *omap_crtc_timings(struct drm_crtc *crtc)
{
	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
	return &omap_crtc->timings;
}

enum omap_channel omap_crtc_channel(struct drm_crtc *crtc)
{
	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
	return omap_crtc->channel;
}

static void omap_crtc_error_irq(struct omap_drm_irq *irq, uint32_t irqstatus)
{
	struct omap_crtc *omap_crtc =
			container_of(irq, struct omap_crtc, error_irq);
	struct drm_crtc *crtc = &omap_crtc->base;
	DRM_ERROR("%s: errors: %08x\n", omap_crtc->name, irqstatus);
	/* avoid getting in a flood, unregister the irq until next vblank */
	__omap_irq_unregister(crtc->dev, &omap_crtc->error_irq);
}

static void omap_crtc_apply_irq(struct omap_drm_irq *irq, uint32_t irqstatus)
{
	struct omap_crtc *omap_crtc =
			container_of(irq, struct omap_crtc, apply_irq);
	struct drm_crtc *crtc = &omap_crtc->base;

	if (!omap_crtc->error_irq.registered)
		__omap_irq_register(crtc->dev, &omap_crtc->error_irq);

	if (!dispc_mgr_go_busy(omap_crtc->channel)) {
		struct omap_drm_private *priv =
				crtc->dev->dev_private;
		DBG("%s: apply done", omap_crtc->name);
		__omap_irq_unregister(crtc->dev, &omap_crtc->apply_irq);
		queue_work(priv->wq, &omap_crtc->apply_work);
	}
}

static void apply_worker(struct work_struct *work)
{
	struct omap_crtc *omap_crtc =
			container_of(work, struct omap_crtc, apply_work);
	struct drm_crtc *crtc = &omap_crtc->base;
	struct drm_device *dev = crtc->dev;
	struct omap_drm_apply *apply, *n;
	bool need_apply;

	/*
	 * Synchronize everything on mode_config.mutex, to keep
	 * the callbacks and list modification all serialized
	 * with respect to modesetting ioctls from userspace.
	 */
	drm_modeset_lock(&crtc->mutex, NULL);
	dispc_runtime_get();

	/*
	 * If we are still pending a previous update, wait.. when the
	 * pending update completes, we get kicked again.
	 */
	if (omap_crtc->apply_irq.registered)
		goto out;

	/* finish up previous apply's: */
	list_for_each_entry_safe(apply, n,
			&omap_crtc->pending_applies, pending_node) {
		apply->post_apply(apply);
		list_del(&apply->pending_node);
	}

	need_apply = !list_empty(&omap_crtc->queued_applies);

	/* then handle the next round of of queued apply's: */
	list_for_each_entry_safe(apply, n,
			&omap_crtc->queued_applies, queued_node) {
		apply->pre_apply(apply);
		list_del(&apply->queued_node);
		apply->queued = false;
		list_add_tail(&apply->pending_node,
				&omap_crtc->pending_applies);
	}

	if (need_apply) {
		enum omap_channel channel = omap_crtc->channel;

		DBG("%s: GO", omap_crtc->name);

		if (dispc_mgr_is_enabled(channel)) {
			omap_irq_register(dev, &omap_crtc->apply_irq);
			dispc_mgr_go(channel);
		} else {
			struct omap_drm_private *priv = dev->dev_private;
			queue_work(priv->wq, &omap_crtc->apply_work);
		}
	}

out:
	dispc_runtime_put();
	drm_modeset_unlock(&crtc->mutex);
}

int omap_crtc_apply(struct drm_crtc *crtc,
		struct omap_drm_apply *apply)
{
	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);

	WARN_ON(!drm_modeset_is_locked(&crtc->mutex));

	/* no need to queue it again if it is already queued: */
	if (apply->queued)
		return 0;

	apply->queued = true;
	list_add_tail(&apply->queued_node, &omap_crtc->queued_applies);

	/*
	 * If there are no currently pending updates, then go ahead and
	 * kick the worker immediately, otherwise it will run again when
	 * the current update finishes.
	 */
	if (list_empty(&omap_crtc->pending_applies)) {
		struct omap_drm_private *priv = crtc->dev->dev_private;
		queue_work(priv->wq, &omap_crtc->apply_work);
	}

	return 0;
}

/* called only from apply */
static void set_enabled(struct drm_crtc *crtc, bool enable)
{
	struct drm_device *dev = crtc->dev;
	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
	enum omap_channel channel = omap_crtc->channel;
	struct omap_irq_wait *wait;
	u32 framedone_irq, vsync_irq;
	int ret;

	if (dispc_mgr_is_enabled(channel) == enable)
		return;

	/*
	 * Digit output produces some sync lost interrupts during the first
	 * frame when enabling, so we need to ignore those.
/* -----------------------------------------------------------------------------
 * Init and Cleanup
 */
	omap_irq_unregister(crtc->dev, &omap_crtc->error_irq);

	framedone_irq = dispc_mgr_get_framedone_irq(channel);
	vsync_irq = dispc_mgr_get_vsync_irq(channel);

	if (enable) {
		wait = omap_irq_wait_init(dev, vsync_irq, 1);
	} else {
		/*
		 * When we disable the digit output, we need to wait for
		 * FRAMEDONE to know that DISPC has finished with the output.
		 *
		 * OMAP2/3 does not have FRAMEDONE irq for digit output, and in
		 * that case we need to use vsync interrupt, and wait for both
		 * even and odd frames.
		 */

		if (framedone_irq)
			wait = omap_irq_wait_init(dev, framedone_irq, 1);
		else
			wait = omap_irq_wait_init(dev, vsync_irq, 2);
	}

	dispc_mgr_enable(channel, enable);

	ret = omap_irq_wait(dev, wait, msecs_to_jiffies(100));
	if (ret) {
		dev_err(dev->dev, "%s: timeout waiting for %s\n",
				omap_crtc->name, enable ? "enable" : "disable");
	}

	omap_irq_register(crtc->dev, &omap_crtc->error_irq);
}

static void omap_crtc_pre_apply(struct omap_drm_apply *apply)
{
	struct omap_crtc *omap_crtc =
			container_of(apply, struct omap_crtc, apply);
	struct drm_crtc *crtc = &omap_crtc->base;
	struct drm_encoder *encoder = NULL;

	DBG("%s: enabled=%d, full=%d", omap_crtc->name,
			omap_crtc->enabled, omap_crtc->full_update);

	if (omap_crtc->full_update) {
		struct omap_drm_private *priv = crtc->dev->dev_private;
		int i;
		for (i = 0; i < priv->num_encoders; i++) {
			if (priv->encoders[i]->crtc == crtc) {
				encoder = priv->encoders[i];
				break;
			}
		}
	}

	if (omap_crtc->current_encoder && encoder != omap_crtc->current_encoder)
		omap_encoder_set_enabled(omap_crtc->current_encoder, false);

	omap_crtc->current_encoder = encoder;

	if (!omap_crtc->enabled) {
		if (encoder)
			omap_encoder_set_enabled(encoder, false);
	} else {
		if (encoder) {
			omap_encoder_set_enabled(encoder, false);
			omap_encoder_update(encoder, omap_crtc->mgr,
					&omap_crtc->timings);
			omap_encoder_set_enabled(encoder, true);
		}
	}

	omap_crtc->full_update = false;
}

static void omap_crtc_post_apply(struct omap_drm_apply *apply)
{
	/* nothing needed for post-apply */
}

void omap_crtc_flush(struct drm_crtc *crtc)
{
	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
	int loops = 0;

	while (!list_empty(&omap_crtc->pending_applies) ||
		!list_empty(&omap_crtc->queued_applies) ||
		omap_crtc->event || omap_crtc->old_fb) {

		if (++loops > 10) {
			dev_err(crtc->dev->dev,
				"omap_crtc_flush() timeout\n");
			break;
		}

		schedule_timeout_uninterruptible(msecs_to_jiffies(20));
	}
}

static const char *channel_names[] = {
	[OMAP_DSS_CHANNEL_LCD] = "lcd",
@@ -681,12 +700,13 @@ struct drm_crtc *omap_crtc_init(struct drm_device *dev,
	struct drm_crtc *crtc = NULL;
	struct omap_crtc *omap_crtc;
	struct omap_overlay_manager_info *info;
	int ret;

	DBG("%s", channel_names[channel]);

	omap_crtc = kzalloc(sizeof(*omap_crtc), GFP_KERNEL);
	if (!omap_crtc)
		goto fail;
		return NULL;

	crtc = &omap_crtc->base;

@@ -700,8 +720,6 @@ struct drm_crtc *omap_crtc_init(struct drm_device *dev,
	omap_crtc->apply.post_apply = omap_crtc_post_apply;

	omap_crtc->channel = channel;
	omap_crtc->plane = plane;
	omap_crtc->plane->crtc = crtc;
	omap_crtc->name = channel_names[channel];
	omap_crtc->pipe = id;

@@ -723,18 +741,18 @@ struct drm_crtc *omap_crtc_init(struct drm_device *dev,
	info->trans_key_type = OMAP_DSS_COLOR_KEY_GFX_DST;
	info->trans_enabled = false;

	drm_crtc_init(dev, crtc, &omap_crtc_funcs);
	ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL,
					&omap_crtc_funcs);
	if (ret < 0) {
		kfree(omap_crtc);
		return NULL;
	}

	drm_crtc_helper_add(crtc, &omap_crtc_helper_funcs);

	omap_plane_install_properties(omap_crtc->plane, &crtc->base);
	omap_plane_install_properties(crtc->primary, &crtc->base);

	omap_crtcs[channel] = omap_crtc;

	return crtc;

fail:
	if (crtc)
		omap_crtc_destroy(crtc);

	return NULL;
}
+7 −1
Original line number Diff line number Diff line
@@ -148,11 +148,15 @@ struct refill_engine {

	bool async;

	wait_queue_head_t wait_for_refill;
	struct completion compl;

	struct list_head idle_node;
};

struct dmm_platform_data {
	uint32_t cpu_cache_flags;
};

struct dmm {
	struct device *dev;
	void __iomem *base;
@@ -183,6 +187,8 @@ struct dmm {

	/* allocation list and lock */
	struct list_head alloc_head;

	const struct dmm_platform_data *plat_data;
};

#endif
+57 −23
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@
#include <linux/mm.h>
#include <linux/time.h>
#include <linux/list.h>
#include <linux/completion.h>

#include "omap_dmm_tiler.h"
#include "omap_dmm_priv.h"
@@ -39,6 +40,10 @@
static struct tcm *containers[TILFMT_NFORMATS];
static struct dmm *omap_dmm;

#if defined(CONFIG_OF)
static const struct of_device_id dmm_of_match[];
#endif

/* global spinlock for protecting lists */
static DEFINE_SPINLOCK(list_lock);

@@ -142,10 +147,10 @@ static irqreturn_t omap_dmm_irq_handler(int irq, void *arg)

	for (i = 0; i < dmm->num_engines; i++) {
		if (status & DMM_IRQSTAT_LST) {
			wake_up_interruptible(&dmm->engines[i].wait_for_refill);

			if (dmm->engines[i].async)
				release_engine(&dmm->engines[i]);

			complete(&dmm->engines[i].compl);
		}

		status >>= 8;
@@ -269,15 +274,17 @@ static int dmm_txn_commit(struct dmm_txn *txn, bool wait)

	/* mark whether it is async to denote list management in IRQ handler */
	engine->async = wait ? false : true;
	reinit_completion(&engine->compl);
	/* verify that the irq handler sees the 'async' and completion value */
	smp_mb();

	/* kick reload */
	writel(engine->refill_pa,
		dmm->base + reg[PAT_DESCR][engine->id]);

	if (wait) {
		if (wait_event_interruptible_timeout(engine->wait_for_refill,
				wait_status(engine, DMM_PATSTATUS_READY) == 0,
				msecs_to_jiffies(1)) <= 0) {
		if (!wait_for_completion_timeout(&engine->compl,
				msecs_to_jiffies(1))) {
			dev_err(dmm->dev, "timed out waiting for done\n");
			ret = -ETIMEDOUT;
		}
@@ -529,6 +536,11 @@ size_t tiler_vsize(enum tiler_fmt fmt, uint16_t w, uint16_t h)
	return round_up(geom[fmt].cpp * w, PAGE_SIZE) * h;
}

uint32_t tiler_get_cpu_cache_flags(void)
{
	return omap_dmm->plat_data->cpu_cache_flags;
}

bool dmm_is_available(void)
{
	return omap_dmm ? true : false;
@@ -592,6 +604,18 @@ static int omap_dmm_probe(struct platform_device *dev)

	init_waitqueue_head(&omap_dmm->engine_queue);

	if (dev->dev.of_node) {
		const struct of_device_id *match;

		match = of_match_node(dmm_of_match, dev->dev.of_node);
		if (!match) {
			dev_err(&dev->dev, "failed to find matching device node\n");
			return -ENODEV;
		}

		omap_dmm->plat_data = match->data;
	}

	/* lookup hwmod data - base address and irq */
	mem = platform_get_resource(dev, IORESOURCE_MEM, 0);
	if (!mem) {
@@ -696,7 +720,7 @@ static int omap_dmm_probe(struct platform_device *dev)
						(REFILL_BUFFER_SIZE * i);
		omap_dmm->engines[i].refill_pa = omap_dmm->refill_pa +
						(REFILL_BUFFER_SIZE * i);
		init_waitqueue_head(&omap_dmm->engines[i].wait_for_refill);
		init_completion(&omap_dmm->engines[i].compl);

		list_add(&omap_dmm->engines[i].idle_node, &omap_dmm->idle_head);
	}
@@ -941,7 +965,7 @@ int tiler_map_show(struct seq_file *s, void *arg)
}
#endif

#ifdef CONFIG_PM
#ifdef CONFIG_PM_SLEEP
static int omap_dmm_resume(struct device *dev)
{
	struct tcm_area area;
@@ -965,16 +989,28 @@ static int omap_dmm_resume(struct device *dev)

	return 0;
}

static const struct dev_pm_ops omap_dmm_pm_ops = {
	.resume = omap_dmm_resume,
};
#endif

static SIMPLE_DEV_PM_OPS(omap_dmm_pm_ops, NULL, omap_dmm_resume);

#if defined(CONFIG_OF)
static const struct dmm_platform_data dmm_omap4_platform_data = {
	.cpu_cache_flags = OMAP_BO_WC,
};

static const struct dmm_platform_data dmm_omap5_platform_data = {
	.cpu_cache_flags = OMAP_BO_UNCACHED,
};

static const struct of_device_id dmm_of_match[] = {
	{ .compatible = "ti,omap4-dmm", },
	{ .compatible = "ti,omap5-dmm", },
	{
		.compatible = "ti,omap4-dmm",
		.data = &dmm_omap4_platform_data,
	},
	{
		.compatible = "ti,omap5-dmm",
		.data = &dmm_omap5_platform_data,
	},
	{},
};
#endif
@@ -986,9 +1022,7 @@ struct platform_driver omap_dmm_driver = {
		.owner = THIS_MODULE,
		.name = DMM_DRIVER_NAME,
		.of_match_table = of_match_ptr(dmm_of_match),
#ifdef CONFIG_PM
		.pm = &omap_dmm_pm_ops,
#endif
	},
};

+1 −0
Original line number Diff line number Diff line
@@ -106,6 +106,7 @@ uint32_t tiler_stride(enum tiler_fmt fmt, uint32_t orient);
size_t tiler_size(enum tiler_fmt fmt, uint16_t w, uint16_t h);
size_t tiler_vsize(enum tiler_fmt fmt, uint16_t w, uint16_t h);
void tiler_align(enum tiler_fmt fmt, uint16_t *w, uint16_t *h);
uint32_t tiler_get_cpu_cache_flags(void);
bool dmm_is_available(void);

extern struct platform_driver omap_dmm_driver;
Loading