summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/rockchip/rockchip_drm_psr.c')
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_psr.c212
1 files changed, 94 insertions, 118 deletions
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.c b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
index 3acfd576b7df..79d00d861a31 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
@@ -18,172 +18,136 @@
#include "rockchip_drm_drv.h"
#include "rockchip_drm_psr.h"
-#define PSR_FLUSH_TIMEOUT msecs_to_jiffies(100)
-
-enum psr_state {
- PSR_FLUSH,
- PSR_ENABLE,
- PSR_DISABLE,
-};
+#define PSR_FLUSH_TIMEOUT_MS 100
struct psr_drv {
struct list_head list;
struct drm_encoder *encoder;
- spinlock_t lock;
- bool active;
- enum psr_state state;
+ struct mutex lock;
+ int inhibit_count;
+ bool enabled;
- struct timer_list flush_timer;
+ struct delayed_work flush_work;
- void (*set)(struct drm_encoder *encoder, bool enable);
+ int (*set)(struct drm_encoder *encoder, bool enable);
};
-static struct psr_drv *find_psr_by_crtc(struct drm_crtc *crtc)
+static struct psr_drv *find_psr_by_encoder(struct drm_encoder *encoder)
{
- struct rockchip_drm_private *drm_drv = crtc->dev->dev_private;
+ struct rockchip_drm_private *drm_drv = encoder->dev->dev_private;
struct psr_drv *psr;
- unsigned long flags;
- spin_lock_irqsave(&drm_drv->psr_list_lock, flags);
+ mutex_lock(&drm_drv->psr_list_lock);
list_for_each_entry(psr, &drm_drv->psr_list, list) {
- if (psr->encoder->crtc == crtc)
+ if (psr->encoder == encoder)
goto out;
}
psr = ERR_PTR(-ENODEV);
out:
- spin_unlock_irqrestore(&drm_drv->psr_list_lock, flags);
+ mutex_unlock(&drm_drv->psr_list_lock);
return psr;
}
-static void psr_set_state_locked(struct psr_drv *psr, enum psr_state state)
+static int psr_set_state_locked(struct psr_drv *psr, bool enable)
{
- /*
- * Allowed finite state machine:
- *
- * PSR_ENABLE < = = = = = > PSR_FLUSH
- * | ^ |
- * | | |
- * v | |
- * PSR_DISABLE < - - - - - - - - -
- */
- if (state == psr->state || !psr->active)
- return;
-
- /* Already disabled in flush, change the state, but not the hardware */
- if (state == PSR_DISABLE && psr->state == PSR_FLUSH) {
- psr->state = state;
- return;
- }
+ int ret;
- psr->state = state;
+ if (psr->inhibit_count > 0)
+ return -EINVAL;
- /* Actually commit the state change to hardware */
- switch (psr->state) {
- case PSR_ENABLE:
- psr->set(psr->encoder, true);
- break;
+ if (enable == psr->enabled)
+ return 0;
- case PSR_DISABLE:
- case PSR_FLUSH:
- psr->set(psr->encoder, false);
- break;
- }
-}
+ ret = psr->set(psr->encoder, enable);
+ if (ret)
+ return ret;
-static void psr_set_state(struct psr_drv *psr, enum psr_state state)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&psr->lock, flags);
- psr_set_state_locked(psr, state);
- spin_unlock_irqrestore(&psr->lock, flags);
+ psr->enabled = enable;
+ return 0;
}
-static void psr_flush_handler(struct timer_list *t)
+static void psr_flush_handler(struct work_struct *work)
{
- struct psr_drv *psr = from_timer(psr, t, flush_timer);
- unsigned long flags;
-
- /* If the state has changed since we initiated the flush, do nothing */
- spin_lock_irqsave(&psr->lock, flags);
- if (psr->state == PSR_FLUSH)
- psr_set_state_locked(psr, PSR_ENABLE);
- spin_unlock_irqrestore(&psr->lock, flags);
+ struct psr_drv *psr = container_of(to_delayed_work(work),
+ struct psr_drv, flush_work);
+
+ mutex_lock(&psr->lock);
+ psr_set_state_locked(psr, true);
+ mutex_unlock(&psr->lock);
}
/**
- * rockchip_drm_psr_activate - activate PSR on the given pipe
- * @crtc: CRTC to obtain the PSR encoder
+ * rockchip_drm_psr_inhibit_put - release PSR inhibit on given encoder
+ * @encoder: encoder to obtain the PSR encoder
+ *
+ * Decrements PSR inhibit count on given encoder. Should be called only
+ * for a PSR inhibit count increment done before. If PSR inhibit counter
+ * reaches zero, PSR flush work is scheduled to make the hardware enter
+ * PSR mode in PSR_FLUSH_TIMEOUT_MS.
*
* Returns:
* Zero on success, negative errno on failure.
*/
-int rockchip_drm_psr_activate(struct drm_crtc *crtc)
+int rockchip_drm_psr_inhibit_put(struct drm_encoder *encoder)
{
- struct psr_drv *psr = find_psr_by_crtc(crtc);
- unsigned long flags;
+ struct psr_drv *psr = find_psr_by_encoder(encoder);
if (IS_ERR(psr))
return PTR_ERR(psr);
- spin_lock_irqsave(&psr->lock, flags);
- psr->active = true;
- spin_unlock_irqrestore(&psr->lock, flags);
+ mutex_lock(&psr->lock);
+ --psr->inhibit_count;
+ WARN_ON(psr->inhibit_count < 0);
+ if (!psr->inhibit_count)
+ mod_delayed_work(system_wq, &psr->flush_work,
+ PSR_FLUSH_TIMEOUT_MS);
+ mutex_unlock(&psr->lock);
return 0;
}
-EXPORT_SYMBOL(rockchip_drm_psr_activate);
+EXPORT_SYMBOL(rockchip_drm_psr_inhibit_put);
/**
- * rockchip_drm_psr_deactivate - deactivate PSR on the given pipe
- * @crtc: CRTC to obtain the PSR encoder
+ * rockchip_drm_psr_inhibit_get - acquire PSR inhibit on given encoder
+ * @encoder: encoder to obtain the PSR encoder
+ *
+ * Increments PSR inhibit count on given encoder. This function guarantees
+ * that after it returns PSR is turned off on given encoder and no PSR-related
+ * hardware state change occurs at least until a matching call to
+ * rockchip_drm_psr_inhibit_put() is done.
*
* Returns:
* Zero on success, negative errno on failure.
*/
-int rockchip_drm_psr_deactivate(struct drm_crtc *crtc)
+int rockchip_drm_psr_inhibit_get(struct drm_encoder *encoder)
{
- struct psr_drv *psr = find_psr_by_crtc(crtc);
- unsigned long flags;
+ struct psr_drv *psr = find_psr_by_encoder(encoder);
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);
+ mutex_lock(&psr->lock);
+ psr_set_state_locked(psr, false);
+ ++psr->inhibit_count;
+ mutex_unlock(&psr->lock);
+ cancel_delayed_work_sync(&psr->flush_work);
return 0;
}
-EXPORT_SYMBOL(rockchip_drm_psr_deactivate);
+EXPORT_SYMBOL(rockchip_drm_psr_inhibit_get);
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);
-}
+ cancel_delayed_work_sync(&psr->flush_work);
-/**
- * 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))
- return PTR_ERR(psr);
-
- rockchip_drm_do_flush(psr);
- return 0;
+ mutex_lock(&psr->lock);
+ if (!psr_set_state_locked(psr, false))
+ mod_delayed_work(system_wq, &psr->flush_work,
+ PSR_FLUSH_TIMEOUT_MS);
+ mutex_unlock(&psr->lock);
}
-EXPORT_SYMBOL(rockchip_drm_psr_flush);
/**
* rockchip_drm_psr_flush_all - force to flush all registered PSR encoders
@@ -201,12 +165,11 @@ void rockchip_drm_psr_flush_all(struct drm_device *dev)
{
struct rockchip_drm_private *drm_drv = dev->dev_private;
struct psr_drv *psr;
- unsigned long flags;
- spin_lock_irqsave(&drm_drv->psr_list_lock, flags);
+ mutex_lock(&drm_drv->psr_list_lock);
list_for_each_entry(psr, &drm_drv->psr_list, list)
rockchip_drm_do_flush(psr);
- spin_unlock_irqrestore(&drm_drv->psr_list_lock, flags);
+ mutex_unlock(&drm_drv->psr_list_lock);
}
EXPORT_SYMBOL(rockchip_drm_psr_flush_all);
@@ -215,15 +178,19 @@ EXPORT_SYMBOL(rockchip_drm_psr_flush_all);
* @encoder: encoder that obtain the PSR function
* @psr_set: call back to set PSR state
*
+ * The function returns with PSR inhibit counter initialized with one
+ * and the caller (typically encoder driver) needs to call
+ * rockchip_drm_psr_inhibit_put() when it becomes ready to accept PSR
+ * enable request.
+ *
* Returns:
* Zero on success, negative errno on failure.
*/
int rockchip_drm_psr_register(struct drm_encoder *encoder,
- void (*psr_set)(struct drm_encoder *, bool enable))
+ int (*psr_set)(struct drm_encoder *, bool enable))
{
struct rockchip_drm_private *drm_drv = encoder->dev->dev_private;
struct psr_drv *psr;
- unsigned long flags;
if (!encoder || !psr_set)
return -EINVAL;
@@ -232,17 +199,17 @@ int rockchip_drm_psr_register(struct drm_encoder *encoder,
if (!psr)
return -ENOMEM;
- timer_setup(&psr->flush_timer, psr_flush_handler, 0);
- spin_lock_init(&psr->lock);
+ INIT_DELAYED_WORK(&psr->flush_work, psr_flush_handler);
+ mutex_init(&psr->lock);
- psr->active = true;
- psr->state = PSR_DISABLE;
+ psr->inhibit_count = 1;
+ psr->enabled = false;
psr->encoder = encoder;
psr->set = psr_set;
- spin_lock_irqsave(&drm_drv->psr_list_lock, flags);
+ mutex_lock(&drm_drv->psr_list_lock);
list_add_tail(&psr->list, &drm_drv->psr_list);
- spin_unlock_irqrestore(&drm_drv->psr_list_lock, flags);
+ mutex_unlock(&drm_drv->psr_list_lock);
return 0;
}
@@ -253,6 +220,11 @@ EXPORT_SYMBOL(rockchip_drm_psr_register);
* @encoder: encoder that obtain the PSR function
* @psr_set: call back to set PSR state
*
+ * It is expected that the PSR inhibit counter is 1 when this function is
+ * called, which corresponds to a state when related encoder has been
+ * disconnected from any CRTCs and its driver called
+ * rockchip_drm_psr_inhibit_get() to stop the PSR logic.
+ *
* Returns:
* Zero on success, negative errno on failure.
*/
@@ -260,16 +232,20 @@ void rockchip_drm_psr_unregister(struct drm_encoder *encoder)
{
struct rockchip_drm_private *drm_drv = encoder->dev->dev_private;
struct psr_drv *psr, *n;
- unsigned long flags;
- spin_lock_irqsave(&drm_drv->psr_list_lock, flags);
+ mutex_lock(&drm_drv->psr_list_lock);
list_for_each_entry_safe(psr, n, &drm_drv->psr_list, list) {
if (psr->encoder == encoder) {
- del_timer(&psr->flush_timer);
+ /*
+ * Any other value would mean that the encoder
+ * is still in use.
+ */
+ WARN_ON(psr->inhibit_count != 1);
+
list_del(&psr->list);
kfree(psr);
}
}
- spin_unlock_irqrestore(&drm_drv->psr_list_lock, flags);
+ mutex_unlock(&drm_drv->psr_list_lock);
}
EXPORT_SYMBOL(rockchip_drm_psr_unregister);
OpenPOWER on IntegriCloud