123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251 |
- /*
- * 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_MS 100
- struct psr_drv {
- struct list_head list;
- struct drm_encoder *encoder;
- struct mutex lock;
- int inhibit_count;
- bool enabled;
- struct delayed_work flush_work;
- int (*set)(struct drm_encoder *encoder, bool enable);
- };
- static struct psr_drv *find_psr_by_encoder(struct drm_encoder *encoder)
- {
- struct rockchip_drm_private *drm_drv = encoder->dev->dev_private;
- struct psr_drv *psr;
- mutex_lock(&drm_drv->psr_list_lock);
- list_for_each_entry(psr, &drm_drv->psr_list, list) {
- if (psr->encoder == encoder)
- goto out;
- }
- psr = ERR_PTR(-ENODEV);
- out:
- mutex_unlock(&drm_drv->psr_list_lock);
- return psr;
- }
- static int psr_set_state_locked(struct psr_drv *psr, bool enable)
- {
- int ret;
- if (psr->inhibit_count > 0)
- return -EINVAL;
- if (enable == psr->enabled)
- return 0;
- ret = psr->set(psr->encoder, enable);
- if (ret)
- return ret;
- psr->enabled = enable;
- return 0;
- }
- static void psr_flush_handler(struct work_struct *work)
- {
- 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_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_inhibit_put(struct drm_encoder *encoder)
- {
- struct psr_drv *psr = find_psr_by_encoder(encoder);
- if (IS_ERR(psr))
- return PTR_ERR(psr);
- 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_inhibit_put);
- /**
- * 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_inhibit_get(struct drm_encoder *encoder)
- {
- struct psr_drv *psr = find_psr_by_encoder(encoder);
- if (IS_ERR(psr))
- return PTR_ERR(psr);
- 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_inhibit_get);
- static void rockchip_drm_do_flush(struct psr_drv *psr)
- {
- cancel_delayed_work_sync(&psr->flush_work);
- 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);
- }
- /**
- * 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;
- mutex_lock(&drm_drv->psr_list_lock);
- list_for_each_entry(psr, &drm_drv->psr_list, list)
- rockchip_drm_do_flush(psr);
- mutex_unlock(&drm_drv->psr_list_lock);
- }
- 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
- *
- * 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,
- int (*psr_set)(struct drm_encoder *, bool enable))
- {
- struct rockchip_drm_private *drm_drv = encoder->dev->dev_private;
- struct psr_drv *psr;
- if (!encoder || !psr_set)
- return -EINVAL;
- psr = kzalloc(sizeof(struct psr_drv), GFP_KERNEL);
- if (!psr)
- return -ENOMEM;
- INIT_DELAYED_WORK(&psr->flush_work, psr_flush_handler);
- mutex_init(&psr->lock);
- psr->inhibit_count = 1;
- psr->enabled = false;
- psr->encoder = encoder;
- psr->set = psr_set;
- mutex_lock(&drm_drv->psr_list_lock);
- list_add_tail(&psr->list, &drm_drv->psr_list);
- mutex_unlock(&drm_drv->psr_list_lock);
- 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
- *
- * 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.
- */
- void rockchip_drm_psr_unregister(struct drm_encoder *encoder)
- {
- struct rockchip_drm_private *drm_drv = encoder->dev->dev_private;
- struct psr_drv *psr, *n;
- mutex_lock(&drm_drv->psr_list_lock);
- list_for_each_entry_safe(psr, n, &drm_drv->psr_list, list) {
- if (psr->encoder == encoder) {
- /*
- * Any other value would mean that the encoder
- * is still in use.
- */
- WARN_ON(psr->inhibit_count != 1);
- list_del(&psr->list);
- kfree(psr);
- }
- }
- mutex_unlock(&drm_drv->psr_list_lock);
- }
- EXPORT_SYMBOL(rockchip_drm_psr_unregister);
|