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

Commit 378db830 authored by Dave Airlie's avatar Dave Airlie
Browse files

Merge branch 'for-next' of ssh://people.freedesktop.org/~seanpaul/dogwood into drm-next

I've included some improvements to PSR from myself, as well as a great
series from Tomasz to clean up and tighten up vblank/flip handling.

The last patch is one from Tomeu that has been floating around for a
while, and since rockchip is one of the beneficiaries, I figured this
would be a good place to pick it up.

* 'for-next' of ssh://people.freedesktop.org/~seanpaul/dogwood:
  drm/rockchip: Balance irq refcount on failure
  drm/rockchip: Kill vop_plane_state
  drm/rockchip: Always signal event in next vblank after cfg_done
  drm/rockchip: Do not enable vblank without event
  drm/rockchip: Replace custom wait_for_vblanks with helper
  drm/rockchip: Unreference framebuffers from flip work
  drm/rockchip: Avoid race with vblank count increment
  drm/rockchip: Get rid of some unnecessary code
  drm/rockchip: Clear interrupt status bits before enabling
  drm/rockchip: Fix up bug in psr state machine
  drm/bridge: analogix_dp: Remove duplicated code
  drm/rockchip: Reduce psr flush time to 100ms
  drm/rockchip: Don't key off vblank for psr
parents 662d5c95 8c763c9b
Loading
Loading
Loading
Loading
+0 −1
Original line number Original line Diff line number Diff line
@@ -39,7 +39,6 @@ struct drm_connector;
struct rockchip_crtc_funcs {
struct rockchip_crtc_funcs {
	int (*enable_vblank)(struct drm_crtc *crtc);
	int (*enable_vblank)(struct drm_crtc *crtc);
	void (*disable_vblank)(struct drm_crtc *crtc);
	void (*disable_vblank)(struct drm_crtc *crtc);
	void (*wait_for_update)(struct drm_crtc *crtc);
};
};


struct rockchip_crtc_state {
struct rockchip_crtc_state {
+2 −64
Original line number Original line Diff line number Diff line
@@ -70,7 +70,7 @@ static int rockchip_drm_fb_dirty(struct drm_framebuffer *fb,
				 struct drm_clip_rect *clips,
				 struct drm_clip_rect *clips,
				 unsigned int num_clips)
				 unsigned int num_clips)
{
{
	rockchip_drm_psr_flush(fb->dev);
	rockchip_drm_psr_flush_all(fb->dev);
	return 0;
	return 0;
}
}


@@ -174,68 +174,6 @@ static void rockchip_drm_output_poll_changed(struct drm_device *dev)
		drm_fb_helper_hotplug_event(fb_helper);
		drm_fb_helper_hotplug_event(fb_helper);
}
}


static void rockchip_crtc_wait_for_update(struct drm_crtc *crtc)
{
	struct rockchip_drm_private *priv = crtc->dev->dev_private;
	int pipe = drm_crtc_index(crtc);
	const struct rockchip_crtc_funcs *crtc_funcs = priv->crtc_funcs[pipe];

	if (crtc_funcs && crtc_funcs->wait_for_update)
		crtc_funcs->wait_for_update(crtc);
}

/*
 * We can't use drm_atomic_helper_wait_for_vblanks() because rk3288 and rk3066
 * have hardware counters for neither vblanks nor scanlines, which results in
 * a race where:
 *				| <-- HW vsync irq and reg take effect
 *	       plane_commit --> |
 *	get_vblank and wait --> |
 *				| <-- handle_vblank, vblank->count + 1
 *		 cleanup_fb --> |
 *		iommu crash --> |
 *				| <-- HW vsync irq and reg take effect
 *
 * This function is equivalent but uses rockchip_crtc_wait_for_update() instead
 * of waiting for vblank_count to change.
 */
static void
rockchip_atomic_wait_for_complete(struct drm_device *dev, struct drm_atomic_state *old_state)
{
	struct drm_crtc_state *old_crtc_state;
	struct drm_crtc *crtc;
	int i, ret;

	for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) {
		/* No one cares about the old state, so abuse it for tracking
		 * and store whether we hold a vblank reference (and should do a
		 * vblank wait) in the ->enable boolean.
		 */
		old_crtc_state->enable = false;

		if (!crtc->state->active)
			continue;

		if (!drm_atomic_helper_framebuffer_changed(dev,
				old_state, crtc))
			continue;

		ret = drm_crtc_vblank_get(crtc);
		if (ret != 0)
			continue;

		old_crtc_state->enable = true;
	}

	for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) {
		if (!old_crtc_state->enable)
			continue;

		rockchip_crtc_wait_for_update(crtc);
		drm_crtc_vblank_put(crtc);
	}
}

static void
static void
rockchip_atomic_commit_tail(struct drm_atomic_state *state)
rockchip_atomic_commit_tail(struct drm_atomic_state *state)
{
{
@@ -250,7 +188,7 @@ rockchip_atomic_commit_tail(struct drm_atomic_state *state)


	drm_atomic_helper_commit_hw_done(state);
	drm_atomic_helper_commit_hw_done(state);


	rockchip_atomic_wait_for_complete(dev, state);
	drm_atomic_helper_wait_for_vblanks(dev, state);


	drm_atomic_helper_cleanup_planes(dev, state);
	drm_atomic_helper_cleanup_planes(dev, state);
}
}
+55 −25
Original line number Original line Diff line number Diff line
@@ -18,7 +18,7 @@
#include "rockchip_drm_drv.h"
#include "rockchip_drm_drv.h"
#include "rockchip_drm_psr.h"
#include "rockchip_drm_psr.h"


#define PSR_FLUSH_TIMEOUT	msecs_to_jiffies(3000) /* 3 seconds */
#define PSR_FLUSH_TIMEOUT	msecs_to_jiffies(100)


enum psr_state {
enum psr_state {
	PSR_FLUSH,
	PSR_FLUSH,
@@ -31,6 +31,7 @@ struct psr_drv {
	struct drm_encoder	*encoder;
	struct drm_encoder	*encoder;


	spinlock_t		lock;
	spinlock_t		lock;
	bool			active;
	enum psr_state		state;
	enum psr_state		state;


	struct timer_list	flush_timer;
	struct timer_list	flush_timer;
@@ -67,19 +68,17 @@ static void psr_set_state_locked(struct psr_drv *psr, enum psr_state state)
	 *       v |                        |
	 *       v |                        |
	 *   PSR_DISABLE < - - - - - - - - -
	 *   PSR_DISABLE < - - - - - - - - -
	 */
	 */
	if (state == psr->state)
	if (state == psr->state || !psr->active)
		return;
		return;


	/* Requesting a flush when disabled is a noop */
	/* Already disabled in flush, change the state, but not the hardware */
	if (state == PSR_FLUSH && psr->state == PSR_DISABLE)
	if (state == PSR_DISABLE && psr->state == PSR_FLUSH) {
		psr->state = state;
		return;
		return;
	}


	psr->state = state;
	psr->state = state;


	/* Already disabled in flush, change the state, but not the hardware */
	if (state == PSR_DISABLE && psr->state == PSR_FLUSH)
		return;

	/* Actually commit the state change to hardware */
	/* Actually commit the state change to hardware */
	switch (psr->state) {
	switch (psr->state) {
	case PSR_ENABLE:
	case PSR_ENABLE:
@@ -115,45 +114,79 @@ static void psr_flush_handler(unsigned long data)
}
}


/**
/**
 * rockchip_drm_psr_enable - enable the encoder PSR which bind to given CRTC
 * rockchip_drm_psr_activate - activate PSR on the given pipe
 * @crtc: CRTC to obtain the PSR encoder
 * @crtc: CRTC to obtain the PSR encoder
 *
 *
 * Returns:
 * Returns:
 * Zero on success, negative errno on failure.
 * Zero on success, negative errno on failure.
 */
 */
int rockchip_drm_psr_enable(struct drm_crtc *crtc)
int rockchip_drm_psr_activate(struct drm_crtc *crtc)
{
{
	struct psr_drv *psr = find_psr_by_crtc(crtc);
	struct psr_drv *psr = find_psr_by_crtc(crtc);
	unsigned long flags;


	if (IS_ERR(psr))
	if (IS_ERR(psr))
		return PTR_ERR(psr);
		return PTR_ERR(psr);


	psr_set_state(psr, PSR_ENABLE);
	spin_lock_irqsave(&psr->lock, flags);
	psr->active = true;
	spin_unlock_irqrestore(&psr->lock, flags);

	return 0;
	return 0;
}
}
EXPORT_SYMBOL(rockchip_drm_psr_enable);
EXPORT_SYMBOL(rockchip_drm_psr_activate);


/**
/**
 * rockchip_drm_psr_disable - disable the encoder PSR which bind to given CRTC
 * rockchip_drm_psr_deactivate - deactivate PSR on the given pipe
 * @crtc: CRTC to obtain the PSR encoder
 * @crtc: CRTC to obtain the PSR encoder
 *
 *
 * Returns:
 * Returns:
 * Zero on success, negative errno on failure.
 * Zero on success, negative errno on failure.
 */
 */
int rockchip_drm_psr_disable(struct drm_crtc *crtc)
int rockchip_drm_psr_deactivate(struct drm_crtc *crtc)
{
{
	struct psr_drv *psr = find_psr_by_crtc(crtc);
	struct psr_drv *psr = find_psr_by_crtc(crtc);
	unsigned long flags;

	if (IS_ERR(psr))
		return PTR_ERR(psr);

	spin_lock_irqsave(&psr->lock, flags);
	psr->active = false;
	spin_unlock_irqrestore(&psr->lock, flags);
	del_timer_sync(&psr->flush_timer);

	return 0;
}
EXPORT_SYMBOL(rockchip_drm_psr_deactivate);


static void rockchip_drm_do_flush(struct psr_drv *psr)
{
	mod_timer(&psr->flush_timer,
		  round_jiffies_up(jiffies + PSR_FLUSH_TIMEOUT));
	psr_set_state(psr, PSR_FLUSH);
}

/**
 * rockchip_drm_psr_flush - flush a single pipe
 * @crtc: CRTC of the pipe to flush
 *
 * Returns:
 * 0 on success, -errno on fail
 */
int rockchip_drm_psr_flush(struct drm_crtc *crtc)
{
	struct psr_drv *psr = find_psr_by_crtc(crtc);
	if (IS_ERR(psr))
	if (IS_ERR(psr))
		return PTR_ERR(psr);
		return PTR_ERR(psr);


	psr_set_state(psr, PSR_DISABLE);
	rockchip_drm_do_flush(psr);
	return 0;
	return 0;
}
}
EXPORT_SYMBOL(rockchip_drm_psr_disable);
EXPORT_SYMBOL(rockchip_drm_psr_flush);


/**
/**
 * rockchip_drm_psr_flush - force to flush all registered PSR encoders
 * rockchip_drm_psr_flush_all - force to flush all registered PSR encoders
 * @dev: drm device
 * @dev: drm device
 *
 *
 * Disable the PSR function for all registered encoders, and then enable the
 * Disable the PSR function for all registered encoders, and then enable the
@@ -164,22 +197,18 @@ EXPORT_SYMBOL(rockchip_drm_psr_disable);
 * Returns:
 * Returns:
 * Zero on success, negative errno on failure.
 * Zero on success, negative errno on failure.
 */
 */
void rockchip_drm_psr_flush(struct drm_device *dev)
void rockchip_drm_psr_flush_all(struct drm_device *dev)
{
{
	struct rockchip_drm_private *drm_drv = dev->dev_private;
	struct rockchip_drm_private *drm_drv = dev->dev_private;
	struct psr_drv *psr;
	struct psr_drv *psr;
	unsigned long flags;
	unsigned long flags;


	spin_lock_irqsave(&drm_drv->psr_list_lock, flags);
	spin_lock_irqsave(&drm_drv->psr_list_lock, flags);
	list_for_each_entry(psr, &drm_drv->psr_list, list) {
	list_for_each_entry(psr, &drm_drv->psr_list, list)
		mod_timer(&psr->flush_timer,
		rockchip_drm_do_flush(psr);
			  round_jiffies_up(jiffies + PSR_FLUSH_TIMEOUT));

		psr_set_state(psr, PSR_FLUSH);
	}
	spin_unlock_irqrestore(&drm_drv->psr_list_lock, flags);
	spin_unlock_irqrestore(&drm_drv->psr_list_lock, flags);
}
}
EXPORT_SYMBOL(rockchip_drm_psr_flush);
EXPORT_SYMBOL(rockchip_drm_psr_flush_all);


/**
/**
 * rockchip_drm_psr_register - register encoder to psr driver
 * rockchip_drm_psr_register - register encoder to psr driver
@@ -206,6 +235,7 @@ int rockchip_drm_psr_register(struct drm_encoder *encoder,
	setup_timer(&psr->flush_timer, psr_flush_handler, (unsigned long)psr);
	setup_timer(&psr->flush_timer, psr_flush_handler, (unsigned long)psr);
	spin_lock_init(&psr->lock);
	spin_lock_init(&psr->lock);


	psr->active = true;
	psr->state = PSR_DISABLE;
	psr->state = PSR_DISABLE;
	psr->encoder = encoder;
	psr->encoder = encoder;
	psr->set = psr_set;
	psr->set = psr_set;
+5 −3
Original line number Original line Diff line number Diff line
@@ -15,9 +15,11 @@
#ifndef __ROCKCHIP_DRM_PSR___
#ifndef __ROCKCHIP_DRM_PSR___
#define __ROCKCHIP_DRM_PSR___
#define __ROCKCHIP_DRM_PSR___


void rockchip_drm_psr_flush(struct drm_device *dev);
void rockchip_drm_psr_flush_all(struct drm_device *dev);
int rockchip_drm_psr_enable(struct drm_crtc *crtc);
int rockchip_drm_psr_flush(struct drm_crtc *crtc);
int rockchip_drm_psr_disable(struct drm_crtc *crtc);

int rockchip_drm_psr_activate(struct drm_crtc *crtc);
int rockchip_drm_psr_deactivate(struct drm_crtc *crtc);


int rockchip_drm_psr_register(struct drm_encoder *encoder,
int rockchip_drm_psr_register(struct drm_encoder *encoder,
			void (*psr_set)(struct drm_encoder *, bool enable));
			void (*psr_set)(struct drm_encoder *, bool enable));
+103 −156
Original line number Original line Diff line number Diff line
@@ -17,12 +17,14 @@
#include <drm/drm_atomic.h>
#include <drm/drm_atomic.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_flip_work.h>
#include <drm/drm_plane_helper.h>
#include <drm/drm_plane_helper.h>


#include <linux/kernel.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/clk.h>
#include <linux/iopoll.h>
#include <linux/of.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_device.h>
#include <linux/pm_runtime.h>
#include <linux/pm_runtime.h>
@@ -86,23 +88,15 @@


#define to_vop(x) container_of(x, struct vop, crtc)
#define to_vop(x) container_of(x, struct vop, crtc)
#define to_vop_win(x) container_of(x, struct vop_win, base)
#define to_vop_win(x) container_of(x, struct vop_win, base)
#define to_vop_plane_state(x) container_of(x, struct vop_plane_state, base)


struct vop_plane_state {
enum vop_pending {
	struct drm_plane_state base;
	VOP_PENDING_FB_UNREF,
	int format;
	dma_addr_t yrgb_mst;
	bool enable;
};
};


struct vop_win {
struct vop_win {
	struct drm_plane base;
	struct drm_plane base;
	const struct vop_win_data *data;
	const struct vop_win_data *data;
	struct vop *vop;
	struct vop *vop;

	/* protected by dev->event_lock */
	bool enable;
	dma_addr_t yrgb_mst;
};
};


struct vop {
struct vop {
@@ -110,17 +104,18 @@ struct vop {
	struct device *dev;
	struct device *dev;
	struct drm_device *drm_dev;
	struct drm_device *drm_dev;
	bool is_enabled;
	bool is_enabled;
	bool vblank_active;


	/* mutex vsync_ work */
	/* mutex vsync_ work */
	struct mutex vsync_mutex;
	struct mutex vsync_mutex;
	bool vsync_work_pending;
	bool vsync_work_pending;
	struct completion dsp_hold_completion;
	struct completion dsp_hold_completion;
	struct completion wait_update_complete;


	/* protected by dev->event_lock */
	/* protected by dev->event_lock */
	struct drm_pending_vblank_event *event;
	struct drm_pending_vblank_event *event;


	struct drm_flip_work fb_unref_work;
	unsigned long pending;

	struct completion line_flag_completion;
	struct completion line_flag_completion;


	const struct vop_data *data;
	const struct vop_data *data;
@@ -414,6 +409,7 @@ static void vop_dsp_hold_valid_irq_enable(struct vop *vop)


	spin_lock_irqsave(&vop->irq_lock, flags);
	spin_lock_irqsave(&vop->irq_lock, flags);


	VOP_INTR_SET_TYPE(vop, clear, DSP_HOLD_VALID_INTR, 1);
	VOP_INTR_SET_TYPE(vop, enable, DSP_HOLD_VALID_INTR, 1);
	VOP_INTR_SET_TYPE(vop, enable, DSP_HOLD_VALID_INTR, 1);


	spin_unlock_irqrestore(&vop->irq_lock, flags);
	spin_unlock_irqrestore(&vop->irq_lock, flags);
@@ -479,6 +475,7 @@ static void vop_line_flag_irq_enable(struct vop *vop, int line_num)
	spin_lock_irqsave(&vop->irq_lock, flags);
	spin_lock_irqsave(&vop->irq_lock, flags);


	VOP_CTRL_SET(vop, line_flag_num[0], line_num);
	VOP_CTRL_SET(vop, line_flag_num[0], line_num);
	VOP_INTR_SET_TYPE(vop, clear, LINE_FLAG_INTR, 1);
	VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 1);
	VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 1);


	spin_unlock_irqrestore(&vop->irq_lock, flags);
	spin_unlock_irqrestore(&vop->irq_lock, flags);
@@ -569,6 +566,8 @@ static void vop_crtc_disable(struct drm_crtc *crtc)


	WARN_ON(vop->event);
	WARN_ON(vop->event);


	rockchip_drm_psr_deactivate(&vop->crtc);

	/*
	/*
	 * We need to make sure that all windows are disabled before we
	 * We need to make sure that all windows are disabled before we
	 * disable that crtc. Otherwise we might try to scan from a destroyed
	 * disable that crtc. Otherwise we might try to scan from a destroyed
@@ -633,22 +632,6 @@ static void vop_plane_destroy(struct drm_plane *plane)
	drm_plane_cleanup(plane);
	drm_plane_cleanup(plane);
}
}


static int vop_plane_prepare_fb(struct drm_plane *plane,
				struct drm_plane_state *new_state)
{
	if (plane->state->fb)
		drm_framebuffer_reference(plane->state->fb);

	return 0;
}

static void vop_plane_cleanup_fb(struct drm_plane *plane,
				 struct drm_plane_state *old_state)
{
	if (old_state->fb)
		drm_framebuffer_unreference(old_state->fb);
}

static int vop_plane_atomic_check(struct drm_plane *plane,
static int vop_plane_atomic_check(struct drm_plane *plane,
			   struct drm_plane_state *state)
			   struct drm_plane_state *state)
{
{
@@ -656,7 +639,6 @@ static int vop_plane_atomic_check(struct drm_plane *plane,
	struct drm_crtc_state *crtc_state;
	struct drm_crtc_state *crtc_state;
	struct drm_framebuffer *fb = state->fb;
	struct drm_framebuffer *fb = state->fb;
	struct vop_win *vop_win = to_vop_win(plane);
	struct vop_win *vop_win = to_vop_win(plane);
	struct vop_plane_state *vop_plane_state = to_vop_plane_state(state);
	const struct vop_win_data *win = vop_win->data;
	const struct vop_win_data *win = vop_win->data;
	int ret;
	int ret;
	struct drm_rect clip;
	struct drm_rect clip;
@@ -666,7 +648,7 @@ static int vop_plane_atomic_check(struct drm_plane *plane,
					DRM_PLANE_HELPER_NO_SCALING;
					DRM_PLANE_HELPER_NO_SCALING;


	if (!crtc || !fb)
	if (!crtc || !fb)
		goto out_disable;
		return 0;


	crtc_state = drm_atomic_get_existing_crtc_state(state->state, crtc);
	crtc_state = drm_atomic_get_existing_crtc_state(state->state, crtc);
	if (WARN_ON(!crtc_state))
	if (WARN_ON(!crtc_state))
@@ -684,11 +666,11 @@ static int vop_plane_atomic_check(struct drm_plane *plane,
		return ret;
		return ret;


	if (!state->visible)
	if (!state->visible)
		goto out_disable;
		return 0;


	vop_plane_state->format = vop_convert_format(fb->pixel_format);
	ret = vop_convert_format(fb->pixel_format);
	if (vop_plane_state->format < 0)
	if (ret < 0)
		return vop_plane_state->format;
		return ret;


	/*
	/*
	 * Src.x1 can be odd when do clip, but yuv plane start point
	 * Src.x1 can be odd when do clip, but yuv plane start point
@@ -697,19 +679,12 @@ static int vop_plane_atomic_check(struct drm_plane *plane,
	if (is_yuv_support(fb->pixel_format) && ((state->src.x1 >> 16) % 2))
	if (is_yuv_support(fb->pixel_format) && ((state->src.x1 >> 16) % 2))
		return -EINVAL;
		return -EINVAL;


	vop_plane_state->enable = true;

	return 0;

out_disable:
	vop_plane_state->enable = false;
	return 0;
	return 0;
}
}


static void vop_plane_atomic_disable(struct drm_plane *plane,
static void vop_plane_atomic_disable(struct drm_plane *plane,
				     struct drm_plane_state *old_state)
				     struct drm_plane_state *old_state)
{
{
	struct vop_plane_state *vop_plane_state = to_vop_plane_state(old_state);
	struct vop_win *vop_win = to_vop_win(plane);
	struct vop_win *vop_win = to_vop_win(plane);
	const struct vop_win_data *win = vop_win->data;
	const struct vop_win_data *win = vop_win->data;
	struct vop *vop = to_vop(old_state->crtc);
	struct vop *vop = to_vop(old_state->crtc);
@@ -717,18 +692,11 @@ static void vop_plane_atomic_disable(struct drm_plane *plane,
	if (!old_state->crtc)
	if (!old_state->crtc)
		return;
		return;


	spin_lock_irq(&plane->dev->event_lock);
	vop_win->enable = false;
	vop_win->yrgb_mst = 0;
	spin_unlock_irq(&plane->dev->event_lock);

	spin_lock(&vop->reg_lock);
	spin_lock(&vop->reg_lock);


	VOP_WIN_SET(vop, win, enable, 0);
	VOP_WIN_SET(vop, win, enable, 0);


	spin_unlock(&vop->reg_lock);
	spin_unlock(&vop->reg_lock);

	vop_plane_state->enable = false;
}
}


static void vop_plane_atomic_update(struct drm_plane *plane,
static void vop_plane_atomic_update(struct drm_plane *plane,
@@ -737,7 +705,6 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
	struct drm_plane_state *state = plane->state;
	struct drm_plane_state *state = plane->state;
	struct drm_crtc *crtc = state->crtc;
	struct drm_crtc *crtc = state->crtc;
	struct vop_win *vop_win = to_vop_win(plane);
	struct vop_win *vop_win = to_vop_win(plane);
	struct vop_plane_state *vop_plane_state = to_vop_plane_state(state);
	const struct vop_win_data *win = vop_win->data;
	const struct vop_win_data *win = vop_win->data;
	struct vop *vop = to_vop(state->crtc);
	struct vop *vop = to_vop(state->crtc);
	struct drm_framebuffer *fb = state->fb;
	struct drm_framebuffer *fb = state->fb;
@@ -752,6 +719,7 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
	dma_addr_t dma_addr;
	dma_addr_t dma_addr;
	uint32_t val;
	uint32_t val;
	bool rb_swap;
	bool rb_swap;
	int format;


	/*
	/*
	 * can't update plane when vop is disabled.
	 * can't update plane when vop is disabled.
@@ -762,7 +730,7 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
	if (WARN_ON(!vop->is_enabled))
	if (WARN_ON(!vop->is_enabled))
		return;
		return;


	if (!vop_plane_state->enable) {
	if (!state->visible) {
		vop_plane_atomic_disable(plane, old_state);
		vop_plane_atomic_disable(plane, old_state);
		return;
		return;
	}
	}
@@ -783,18 +751,15 @@ static void vop_plane_atomic_update(struct drm_plane *plane,


	offset = (src->x1 >> 16) * drm_format_plane_cpp(fb->pixel_format, 0);
	offset = (src->x1 >> 16) * drm_format_plane_cpp(fb->pixel_format, 0);
	offset += (src->y1 >> 16) * fb->pitches[0];
	offset += (src->y1 >> 16) * fb->pitches[0];
	vop_plane_state->yrgb_mst = rk_obj->dma_addr + offset + fb->offsets[0];
	dma_addr = rk_obj->dma_addr + offset + fb->offsets[0];


	spin_lock_irq(&plane->dev->event_lock);
	format = vop_convert_format(fb->pixel_format);
	vop_win->enable = true;
	vop_win->yrgb_mst = vop_plane_state->yrgb_mst;
	spin_unlock_irq(&plane->dev->event_lock);


	spin_lock(&vop->reg_lock);
	spin_lock(&vop->reg_lock);


	VOP_WIN_SET(vop, win, format, vop_plane_state->format);
	VOP_WIN_SET(vop, win, format, format);
	VOP_WIN_SET(vop, win, yrgb_vir, fb->pitches[0] >> 2);
	VOP_WIN_SET(vop, win, yrgb_vir, fb->pitches[0] >> 2);
	VOP_WIN_SET(vop, win, yrgb_mst, vop_plane_state->yrgb_mst);
	VOP_WIN_SET(vop, win, yrgb_mst, dma_addr);
	if (is_yuv_support(fb->pixel_format)) {
	if (is_yuv_support(fb->pixel_format)) {
		int hsub = drm_format_horz_chroma_subsampling(fb->pixel_format);
		int hsub = drm_format_horz_chroma_subsampling(fb->pixel_format);
		int vsub = drm_format_vert_chroma_subsampling(fb->pixel_format);
		int vsub = drm_format_vert_chroma_subsampling(fb->pixel_format);
@@ -841,68 +806,18 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
}
}


static const struct drm_plane_helper_funcs plane_helper_funcs = {
static const struct drm_plane_helper_funcs plane_helper_funcs = {
	.prepare_fb = vop_plane_prepare_fb,
	.cleanup_fb = vop_plane_cleanup_fb,
	.atomic_check = vop_plane_atomic_check,
	.atomic_check = vop_plane_atomic_check,
	.atomic_update = vop_plane_atomic_update,
	.atomic_update = vop_plane_atomic_update,
	.atomic_disable = vop_plane_atomic_disable,
	.atomic_disable = vop_plane_atomic_disable,
};
};


static void vop_atomic_plane_reset(struct drm_plane *plane)
{
	struct vop_plane_state *vop_plane_state =
					to_vop_plane_state(plane->state);

	if (plane->state && plane->state->fb)
		drm_framebuffer_unreference(plane->state->fb);

	kfree(vop_plane_state);
	vop_plane_state = kzalloc(sizeof(*vop_plane_state), GFP_KERNEL);
	if (!vop_plane_state)
		return;

	plane->state = &vop_plane_state->base;
	plane->state->plane = plane;
}

static struct drm_plane_state *
vop_atomic_plane_duplicate_state(struct drm_plane *plane)
{
	struct vop_plane_state *old_vop_plane_state;
	struct vop_plane_state *vop_plane_state;

	if (WARN_ON(!plane->state))
		return NULL;

	old_vop_plane_state = to_vop_plane_state(plane->state);
	vop_plane_state = kmemdup(old_vop_plane_state,
				  sizeof(*vop_plane_state), GFP_KERNEL);
	if (!vop_plane_state)
		return NULL;

	__drm_atomic_helper_plane_duplicate_state(plane,
						  &vop_plane_state->base);

	return &vop_plane_state->base;
}

static void vop_atomic_plane_destroy_state(struct drm_plane *plane,
					   struct drm_plane_state *state)
{
	struct vop_plane_state *vop_state = to_vop_plane_state(state);

	__drm_atomic_helper_plane_destroy_state(state);

	kfree(vop_state);
}

static const struct drm_plane_funcs vop_plane_funcs = {
static const struct drm_plane_funcs vop_plane_funcs = {
	.update_plane	= drm_atomic_helper_update_plane,
	.update_plane	= drm_atomic_helper_update_plane,
	.disable_plane	= drm_atomic_helper_disable_plane,
	.disable_plane	= drm_atomic_helper_disable_plane,
	.destroy = vop_plane_destroy,
	.destroy = vop_plane_destroy,
	.reset = vop_atomic_plane_reset,
	.reset = drm_atomic_helper_plane_reset,
	.atomic_duplicate_state = vop_atomic_plane_duplicate_state,
	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
	.atomic_destroy_state = vop_atomic_plane_destroy_state,
	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
};
};


static int vop_crtc_enable_vblank(struct drm_crtc *crtc)
static int vop_crtc_enable_vblank(struct drm_crtc *crtc)
@@ -915,12 +830,11 @@ static int vop_crtc_enable_vblank(struct drm_crtc *crtc)


	spin_lock_irqsave(&vop->irq_lock, flags);
	spin_lock_irqsave(&vop->irq_lock, flags);


	VOP_INTR_SET_TYPE(vop, clear, FS_INTR, 1);
	VOP_INTR_SET_TYPE(vop, enable, FS_INTR, 1);
	VOP_INTR_SET_TYPE(vop, enable, FS_INTR, 1);


	spin_unlock_irqrestore(&vop->irq_lock, flags);
	spin_unlock_irqrestore(&vop->irq_lock, flags);


	rockchip_drm_psr_disable(&vop->crtc);

	return 0;
	return 0;
}
}


@@ -937,22 +851,11 @@ static void vop_crtc_disable_vblank(struct drm_crtc *crtc)
	VOP_INTR_SET_TYPE(vop, enable, FS_INTR, 0);
	VOP_INTR_SET_TYPE(vop, enable, FS_INTR, 0);


	spin_unlock_irqrestore(&vop->irq_lock, flags);
	spin_unlock_irqrestore(&vop->irq_lock, flags);

	rockchip_drm_psr_enable(&vop->crtc);
}

static void vop_crtc_wait_for_update(struct drm_crtc *crtc)
{
	struct vop *vop = to_vop(crtc);

	reinit_completion(&vop->wait_update_complete);
	WARN_ON(!wait_for_completion_timeout(&vop->wait_update_complete, 100));
}
}


static const struct rockchip_crtc_funcs private_crtc_funcs = {
static const struct rockchip_crtc_funcs private_crtc_funcs = {
	.enable_vblank = vop_crtc_enable_vblank,
	.enable_vblank = vop_crtc_enable_vblank,
	.disable_vblank = vop_crtc_disable_vblank,
	.disable_vblank = vop_crtc_disable_vblank,
	.wait_for_update = vop_crtc_wait_for_update,
};
};


static bool vop_crtc_mode_fixup(struct drm_crtc *crtc,
static bool vop_crtc_mode_fixup(struct drm_crtc *crtc,
@@ -1072,12 +975,44 @@ static void vop_crtc_enable(struct drm_crtc *crtc)
	clk_set_rate(vop->dclk, adjusted_mode->clock * 1000);
	clk_set_rate(vop->dclk, adjusted_mode->clock * 1000);


	VOP_CTRL_SET(vop, standby, 0);
	VOP_CTRL_SET(vop, standby, 0);

	rockchip_drm_psr_activate(&vop->crtc);
}

static bool vop_fs_irq_is_pending(struct vop *vop)
{
	return VOP_INTR_GET_TYPE(vop, status, FS_INTR);
}

static void vop_wait_for_irq_handler(struct vop *vop)
{
	bool pending;
	int ret;

	/*
	 * Spin until frame start interrupt status bit goes low, which means
	 * that interrupt handler was invoked and cleared it. The timeout of
	 * 10 msecs is really too long, but it is just a safety measure if
	 * something goes really wrong. The wait will only happen in the very
	 * unlikely case of a vblank happening exactly at the same time and
	 * shouldn't exceed microseconds range.
	 */
	ret = readx_poll_timeout_atomic(vop_fs_irq_is_pending, vop, pending,
					!pending, 0, 10 * 1000);
	if (ret)
		DRM_DEV_ERROR(vop->dev, "VOP vblank IRQ stuck for 10 ms\n");

	synchronize_irq(vop->irq);
}
}


static void vop_crtc_atomic_flush(struct drm_crtc *crtc,
static void vop_crtc_atomic_flush(struct drm_crtc *crtc,
				  struct drm_crtc_state *old_crtc_state)
				  struct drm_crtc_state *old_crtc_state)
{
{
	struct drm_atomic_state *old_state = old_crtc_state->state;
	struct drm_plane_state *old_plane_state;
	struct vop *vop = to_vop(crtc);
	struct vop *vop = to_vop(crtc);
	struct drm_plane *plane;
	int i;


	if (WARN_ON(!vop->is_enabled))
	if (WARN_ON(!vop->is_enabled))
		return;
		return;
@@ -1087,23 +1022,42 @@ static void vop_crtc_atomic_flush(struct drm_crtc *crtc,
	vop_cfg_done(vop);
	vop_cfg_done(vop);


	spin_unlock(&vop->reg_lock);
	spin_unlock(&vop->reg_lock);
}


static void vop_crtc_atomic_begin(struct drm_crtc *crtc,
	/*
				  struct drm_crtc_state *old_crtc_state)
	 * There is a (rather unlikely) possiblity that a vblank interrupt
{
	 * fired before we set the cfg_done bit. To avoid spuriously
	struct vop *vop = to_vop(crtc);
	 * signalling flip completion we need to wait for it to finish.
	 */
	vop_wait_for_irq_handler(vop);


	spin_lock_irq(&crtc->dev->event_lock);
	spin_lock_irq(&crtc->dev->event_lock);
	vop->vblank_active = true;
	if (crtc->state->event) {
		WARN_ON(drm_crtc_vblank_get(crtc) != 0);
		WARN_ON(drm_crtc_vblank_get(crtc) != 0);
		WARN_ON(vop->event);
		WARN_ON(vop->event);


	if (crtc->state->event) {
		vop->event = crtc->state->event;
		vop->event = crtc->state->event;
		crtc->state->event = NULL;
		crtc->state->event = NULL;
	}
	}
	spin_unlock_irq(&crtc->dev->event_lock);
	spin_unlock_irq(&crtc->dev->event_lock);

	for_each_plane_in_state(old_state, plane, old_plane_state, i) {
		if (!old_plane_state->fb)
			continue;

		if (old_plane_state->fb == plane->state->fb)
			continue;

		drm_framebuffer_reference(old_plane_state->fb);
		drm_flip_work_queue(&vop->fb_unref_work, old_plane_state->fb);
		set_bit(VOP_PENDING_FB_UNREF, &vop->pending);
		WARN_ON(drm_crtc_vblank_get(crtc) != 0);
	}
}

static void vop_crtc_atomic_begin(struct drm_crtc *crtc,
				  struct drm_crtc_state *old_crtc_state)
{
	rockchip_drm_psr_flush(crtc);
}
}


static const struct drm_crtc_helper_funcs vop_crtc_helper_funcs = {
static const struct drm_crtc_helper_funcs vop_crtc_helper_funcs = {
@@ -1160,16 +1114,13 @@ static const struct drm_crtc_funcs vop_crtc_funcs = {
	.atomic_destroy_state = vop_crtc_destroy_state,
	.atomic_destroy_state = vop_crtc_destroy_state,
};
};


static bool vop_win_pending_is_complete(struct vop_win *vop_win)
static void vop_fb_unref_worker(struct drm_flip_work *work, void *val)
{
{
	dma_addr_t yrgb_mst;
	struct vop *vop = container_of(work, struct vop, fb_unref_work);
	struct drm_framebuffer *fb = val;


	if (!vop_win->enable)
	drm_crtc_vblank_put(&vop->crtc);
		return VOP_WIN_GET(vop_win->vop, vop_win->data, enable) == 0;
	drm_framebuffer_unreference(fb);

	yrgb_mst = VOP_WIN_GET_YRGBADDR(vop_win->vop, vop_win->data);

	return yrgb_mst == vop_win->yrgb_mst;
}
}


static void vop_handle_vblank(struct vop *vop)
static void vop_handle_vblank(struct vop *vop)
@@ -1177,27 +1128,17 @@ static void vop_handle_vblank(struct vop *vop)
	struct drm_device *drm = vop->drm_dev;
	struct drm_device *drm = vop->drm_dev;
	struct drm_crtc *crtc = &vop->crtc;
	struct drm_crtc *crtc = &vop->crtc;
	unsigned long flags;
	unsigned long flags;
	int i;

	for (i = 0; i < vop->data->win_size; i++) {
		if (!vop_win_pending_is_complete(&vop->win[i]))
			return;
	}


	spin_lock_irqsave(&drm->event_lock, flags);
	spin_lock_irqsave(&drm->event_lock, flags);
	if (vop->event) {
	if (vop->event) {
		drm_crtc_send_vblank_event(crtc, vop->event);
		drm_crtc_send_vblank_event(crtc, vop->event);
		vop->event = NULL;

	}
	if (vop->vblank_active) {
		vop->vblank_active = false;
		drm_crtc_vblank_put(crtc);
		drm_crtc_vblank_put(crtc);
		vop->event = NULL;
	}
	}
	spin_unlock_irqrestore(&drm->event_lock, flags);
	spin_unlock_irqrestore(&drm->event_lock, flags);


	if (!completion_done(&vop->wait_update_complete))
	if (test_and_clear_bit(VOP_PENDING_FB_UNREF, &vop->pending))
		complete(&vop->wait_update_complete);
		drm_flip_work_commit(&vop->fb_unref_work, system_unbound_wq);
}
}


static irqreturn_t vop_isr(int irq, void *data)
static irqreturn_t vop_isr(int irq, void *data)
@@ -1336,8 +1277,10 @@ static int vop_create_crtc(struct vop *vop)
		goto err_cleanup_crtc;
		goto err_cleanup_crtc;
	}
	}


	drm_flip_work_init(&vop->fb_unref_work, "fb_unref",
			   vop_fb_unref_worker);

	init_completion(&vop->dsp_hold_completion);
	init_completion(&vop->dsp_hold_completion);
	init_completion(&vop->wait_update_complete);
	init_completion(&vop->line_flag_completion);
	init_completion(&vop->line_flag_completion);
	crtc->port = port;
	crtc->port = port;
	rockchip_register_crtc_funcs(crtc, &private_crtc_funcs);
	rockchip_register_crtc_funcs(crtc, &private_crtc_funcs);
@@ -1379,6 +1322,7 @@ static void vop_destroy_crtc(struct vop *vop)
	 * references the CRTC.
	 * references the CRTC.
	 */
	 */
	drm_crtc_cleanup(crtc);
	drm_crtc_cleanup(crtc);
	drm_flip_work_cleanup(&vop->fb_unref_work);
}
}


static int vop_initial(struct vop *vop)
static int vop_initial(struct vop *vop)
@@ -1466,7 +1410,6 @@ static int vop_initial(struct vop *vop)
	clk_disable(vop->aclk);
	clk_disable(vop->aclk);


	vop->is_enabled = false;
	vop->is_enabled = false;
	vop->vblank_active = false;


	return 0;
	return 0;


@@ -1604,11 +1547,15 @@ static int vop_bind(struct device *dev, struct device *master, void *data)


	ret = vop_create_crtc(vop);
	ret = vop_create_crtc(vop);
	if (ret)
	if (ret)
		return ret;
		goto err_enable_irq;


	pm_runtime_enable(&pdev->dev);
	pm_runtime_enable(&pdev->dev);


	return 0;
	return 0;

err_enable_irq:
	enable_irq(vop->irq); /* To balance out the disable_irq above */
	return ret;
}
}


static void vop_unbind(struct device *dev, struct device *master, void *data)
static void vop_unbind(struct device *dev, struct device *master, void *data)