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 v4.19 251 lines 6.7 kB view raw
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);