123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275 |
- /*
- * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
- * Author: Yakir Yang <ykk@rock-chips.com>
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
- #include <drm/drmP.h>
- #include <drm/drm_crtc_helper.h>
- #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,
- };
- struct psr_drv {
- struct list_head list;
- struct drm_encoder *encoder;
- spinlock_t lock;
- bool active;
- enum psr_state state;
- struct timer_list flush_timer;
- void (*set)(struct drm_encoder *encoder, bool enable);
- };
- static struct psr_drv *find_psr_by_crtc(struct drm_crtc *crtc)
- {
- struct rockchip_drm_private *drm_drv = crtc->dev->dev_private;
- struct psr_drv *psr;
- unsigned long flags;
- spin_lock_irqsave(&drm_drv->psr_list_lock, flags);
- list_for_each_entry(psr, &drm_drv->psr_list, list) {
- if (psr->encoder->crtc == crtc)
- goto out;
- }
- psr = ERR_PTR(-ENODEV);
- out:
- spin_unlock_irqrestore(&drm_drv->psr_list_lock, flags);
- return psr;
- }
- static void psr_set_state_locked(struct psr_drv *psr, enum psr_state state)
- {
- /*
- * 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;
- }
- psr->state = state;
- /* Actually commit the state change to hardware */
- switch (psr->state) {
- case PSR_ENABLE:
- psr->set(psr->encoder, true);
- break;
- case PSR_DISABLE:
- case PSR_FLUSH:
- psr->set(psr->encoder, false);
- break;
- }
- }
- 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);
- }
- static void psr_flush_handler(struct timer_list *t)
- {
- 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);
- }
- /**
- * rockchip_drm_psr_activate - activate PSR on the given pipe
- * @crtc: CRTC to obtain the PSR encoder
- *
- * Returns:
- * Zero on success, negative errno on failure.
- */
- int rockchip_drm_psr_activate(struct drm_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 = true;
- spin_unlock_irqrestore(&psr->lock, flags);
- return 0;
- }
- EXPORT_SYMBOL(rockchip_drm_psr_activate);
- /**
- * rockchip_drm_psr_deactivate - deactivate PSR on the given pipe
- * @crtc: CRTC to obtain the PSR encoder
- *
- * Returns:
- * Zero on success, negative errno on failure.
- */
- int rockchip_drm_psr_deactivate(struct drm_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))
- return PTR_ERR(psr);
- rockchip_drm_do_flush(psr);
- return 0;
- }
- EXPORT_SYMBOL(rockchip_drm_psr_flush);
- /**
- * rockchip_drm_psr_flush_all - force to flush all registered PSR encoders
- * @dev: drm device
- *
- * Disable the PSR function for all registered encoders, and then enable the
- * PSR function back after PSR_FLUSH_TIMEOUT. If encoder PSR state have been
- * changed during flush time, then keep the state no change after flush
- * timeout.
- *
- * Returns:
- * Zero on success, negative errno on failure.
- */
- 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);
- list_for_each_entry(psr, &drm_drv->psr_list, list)
- rockchip_drm_do_flush(psr);
- spin_unlock_irqrestore(&drm_drv->psr_list_lock, flags);
- }
- EXPORT_SYMBOL(rockchip_drm_psr_flush_all);
- /**
- * rockchip_drm_psr_register - register encoder to psr driver
- * @encoder: encoder that obtain the PSR function
- * @psr_set: call back to set PSR state
- *
- * 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))
- {
- struct rockchip_drm_private *drm_drv = encoder->dev->dev_private;
- struct psr_drv *psr;
- unsigned long flags;
- if (!encoder || !psr_set)
- return -EINVAL;
- psr = kzalloc(sizeof(struct psr_drv), GFP_KERNEL);
- if (!psr)
- return -ENOMEM;
- timer_setup(&psr->flush_timer, psr_flush_handler, 0);
- spin_lock_init(&psr->lock);
- psr->active = true;
- psr->state = PSR_DISABLE;
- psr->encoder = encoder;
- psr->set = psr_set;
- spin_lock_irqsave(&drm_drv->psr_list_lock, flags);
- list_add_tail(&psr->list, &drm_drv->psr_list);
- spin_unlock_irqrestore(&drm_drv->psr_list_lock, flags);
- return 0;
- }
- EXPORT_SYMBOL(rockchip_drm_psr_register);
- /**
- * rockchip_drm_psr_unregister - unregister encoder to psr driver
- * @encoder: encoder that obtain the PSR function
- * @psr_set: call back to set PSR state
- *
- * Returns:
- * Zero on success, negative errno on failure.
- */
- 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);
- list_for_each_entry_safe(psr, n, &drm_drv->psr_list, list) {
- if (psr->encoder == encoder) {
- del_timer(&psr->flush_timer);
- list_del(&psr->list);
- kfree(psr);
- }
- }
- spin_unlock_irqrestore(&drm_drv->psr_list_lock, flags);
- }
- EXPORT_SYMBOL(rockchip_drm_psr_unregister);
|