linux/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
   3 * Author: Yakir Yang <ykk@rock-chips.com>
   4 *
   5 * This software is licensed under the terms of the GNU General Public
   6 * License version 2, as published by the Free Software Foundation, and
   7 * may be copied, distributed, and modified under those terms.
   8 *
   9 * This program is distributed in the hope that it will be useful,
  10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12 * GNU General Public License for more details.
  13 */
  14
  15#include <drm/drmP.h>
  16#include <drm/drm_crtc_helper.h>
  17
  18#include "rockchip_drm_drv.h"
  19#include "rockchip_drm_psr.h"
  20
  21#define PSR_FLUSH_TIMEOUT_MS    100
  22
  23struct psr_drv {
  24        struct list_head        list;
  25        struct drm_encoder      *encoder;
  26
  27        struct mutex            lock;
  28        int                     inhibit_count;
  29        bool                    enabled;
  30
  31        struct delayed_work     flush_work;
  32
  33        int (*set)(struct drm_encoder *encoder, bool enable);
  34};
  35
  36static struct psr_drv *find_psr_by_encoder(struct drm_encoder *encoder)
  37{
  38        struct rockchip_drm_private *drm_drv = encoder->dev->dev_private;
  39        struct psr_drv *psr;
  40
  41        mutex_lock(&drm_drv->psr_list_lock);
  42        list_for_each_entry(psr, &drm_drv->psr_list, list) {
  43                if (psr->encoder == encoder)
  44                        goto out;
  45        }
  46        psr = ERR_PTR(-ENODEV);
  47
  48out:
  49        mutex_unlock(&drm_drv->psr_list_lock);
  50        return psr;
  51}
  52
  53static int psr_set_state_locked(struct psr_drv *psr, bool enable)
  54{
  55        int ret;
  56
  57        if (psr->inhibit_count > 0)
  58                return -EINVAL;
  59
  60        if (enable == psr->enabled)
  61                return 0;
  62
  63        ret = psr->set(psr->encoder, enable);
  64        if (ret)
  65                return ret;
  66
  67        psr->enabled = enable;
  68        return 0;
  69}
  70
  71static void psr_flush_handler(struct work_struct *work)
  72{
  73        struct psr_drv *psr = container_of(to_delayed_work(work),
  74                                           struct psr_drv, flush_work);
  75
  76        mutex_lock(&psr->lock);
  77        psr_set_state_locked(psr, true);
  78        mutex_unlock(&psr->lock);
  79}
  80
  81/**
  82 * rockchip_drm_psr_inhibit_put - release PSR inhibit on given encoder
  83 * @encoder: encoder to obtain the PSR encoder
  84 *
  85 * Decrements PSR inhibit count on given encoder. Should be called only
  86 * for a PSR inhibit count increment done before. If PSR inhibit counter
  87 * reaches zero, PSR flush work is scheduled to make the hardware enter
  88 * PSR mode in PSR_FLUSH_TIMEOUT_MS.
  89 *
  90 * Returns:
  91 * Zero on success, negative errno on failure.
  92 */
  93int rockchip_drm_psr_inhibit_put(struct drm_encoder *encoder)
  94{
  95        struct psr_drv *psr = find_psr_by_encoder(encoder);
  96
  97        if (IS_ERR(psr))
  98                return PTR_ERR(psr);
  99
 100        mutex_lock(&psr->lock);
 101        --psr->inhibit_count;
 102        WARN_ON(psr->inhibit_count < 0);
 103        if (!psr->inhibit_count)
 104                mod_delayed_work(system_wq, &psr->flush_work,
 105                                 PSR_FLUSH_TIMEOUT_MS);
 106        mutex_unlock(&psr->lock);
 107
 108        return 0;
 109}
 110EXPORT_SYMBOL(rockchip_drm_psr_inhibit_put);
 111
 112/**
 113 * rockchip_drm_psr_inhibit_get - acquire PSR inhibit on given encoder
 114 * @encoder: encoder to obtain the PSR encoder
 115 *
 116 * Increments PSR inhibit count on given encoder. This function guarantees
 117 * that after it returns PSR is turned off on given encoder and no PSR-related
 118 * hardware state change occurs at least until a matching call to
 119 * rockchip_drm_psr_inhibit_put() is done.
 120 *
 121 * Returns:
 122 * Zero on success, negative errno on failure.
 123 */
 124int rockchip_drm_psr_inhibit_get(struct drm_encoder *encoder)
 125{
 126        struct psr_drv *psr = find_psr_by_encoder(encoder);
 127
 128        if (IS_ERR(psr))
 129                return PTR_ERR(psr);
 130
 131        mutex_lock(&psr->lock);
 132        psr_set_state_locked(psr, false);
 133        ++psr->inhibit_count;
 134        mutex_unlock(&psr->lock);
 135        cancel_delayed_work_sync(&psr->flush_work);
 136
 137        return 0;
 138}
 139EXPORT_SYMBOL(rockchip_drm_psr_inhibit_get);
 140
 141static void rockchip_drm_do_flush(struct psr_drv *psr)
 142{
 143        cancel_delayed_work_sync(&psr->flush_work);
 144
 145        mutex_lock(&psr->lock);
 146        if (!psr_set_state_locked(psr, false))
 147                mod_delayed_work(system_wq, &psr->flush_work,
 148                                 PSR_FLUSH_TIMEOUT_MS);
 149        mutex_unlock(&psr->lock);
 150}
 151
 152/**
 153 * rockchip_drm_psr_flush_all - force to flush all registered PSR encoders
 154 * @dev: drm device
 155 *
 156 * Disable the PSR function for all registered encoders, and then enable the
 157 * PSR function back after PSR_FLUSH_TIMEOUT. If encoder PSR state have been
 158 * changed during flush time, then keep the state no change after flush
 159 * timeout.
 160 *
 161 * Returns:
 162 * Zero on success, negative errno on failure.
 163 */
 164void rockchip_drm_psr_flush_all(struct drm_device *dev)
 165{
 166        struct rockchip_drm_private *drm_drv = dev->dev_private;
 167        struct psr_drv *psr;
 168
 169        mutex_lock(&drm_drv->psr_list_lock);
 170        list_for_each_entry(psr, &drm_drv->psr_list, list)
 171                rockchip_drm_do_flush(psr);
 172        mutex_unlock(&drm_drv->psr_list_lock);
 173}
 174EXPORT_SYMBOL(rockchip_drm_psr_flush_all);
 175
 176/**
 177 * rockchip_drm_psr_register - register encoder to psr driver
 178 * @encoder: encoder that obtain the PSR function
 179 * @psr_set: call back to set PSR state
 180 *
 181 * The function returns with PSR inhibit counter initialized with one
 182 * and the caller (typically encoder driver) needs to call
 183 * rockchip_drm_psr_inhibit_put() when it becomes ready to accept PSR
 184 * enable request.
 185 *
 186 * Returns:
 187 * Zero on success, negative errno on failure.
 188 */
 189int rockchip_drm_psr_register(struct drm_encoder *encoder,
 190                        int (*psr_set)(struct drm_encoder *, bool enable))
 191{
 192        struct rockchip_drm_private *drm_drv = encoder->dev->dev_private;
 193        struct psr_drv *psr;
 194
 195        if (!encoder || !psr_set)
 196                return -EINVAL;
 197
 198        psr = kzalloc(sizeof(struct psr_drv), GFP_KERNEL);
 199        if (!psr)
 200                return -ENOMEM;
 201
 202        INIT_DELAYED_WORK(&psr->flush_work, psr_flush_handler);
 203        mutex_init(&psr->lock);
 204
 205        psr->inhibit_count = 1;
 206        psr->enabled = false;
 207        psr->encoder = encoder;
 208        psr->set = psr_set;
 209
 210        mutex_lock(&drm_drv->psr_list_lock);
 211        list_add_tail(&psr->list, &drm_drv->psr_list);
 212        mutex_unlock(&drm_drv->psr_list_lock);
 213
 214        return 0;
 215}
 216EXPORT_SYMBOL(rockchip_drm_psr_register);
 217
 218/**
 219 * rockchip_drm_psr_unregister - unregister encoder to psr driver
 220 * @encoder: encoder that obtain the PSR function
 221 * @psr_set: call back to set PSR state
 222 *
 223 * It is expected that the PSR inhibit counter is 1 when this function is
 224 * called, which corresponds to a state when related encoder has been
 225 * disconnected from any CRTCs and its driver called
 226 * rockchip_drm_psr_inhibit_get() to stop the PSR logic.
 227 *
 228 * Returns:
 229 * Zero on success, negative errno on failure.
 230 */
 231void rockchip_drm_psr_unregister(struct drm_encoder *encoder)
 232{
 233        struct rockchip_drm_private *drm_drv = encoder->dev->dev_private;
 234        struct psr_drv *psr, *n;
 235
 236        mutex_lock(&drm_drv->psr_list_lock);
 237        list_for_each_entry_safe(psr, n, &drm_drv->psr_list, list) {
 238                if (psr->encoder == encoder) {
 239                        /*
 240                         * Any other value would mean that the encoder
 241                         * is still in use.
 242                         */
 243                        WARN_ON(psr->inhibit_count != 1);
 244
 245                        list_del(&psr->list);
 246                        kfree(psr);
 247                }
 248        }
 249        mutex_unlock(&drm_drv->psr_list_lock);
 250}
 251EXPORT_SYMBOL(rockchip_drm_psr_unregister);
 252