Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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