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.11 275 lines 6.9 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 msecs_to_jiffies(100) 22 23enum psr_state { 24 PSR_FLUSH, 25 PSR_ENABLE, 26 PSR_DISABLE, 27}; 28 29struct psr_drv { 30 struct list_head list; 31 struct drm_encoder *encoder; 32 33 spinlock_t lock; 34 bool active; 35 enum psr_state state; 36 37 struct timer_list flush_timer; 38 39 void (*set)(struct drm_encoder *encoder, bool enable); 40}; 41 42static struct psr_drv *find_psr_by_crtc(struct drm_crtc *crtc) 43{ 44 struct rockchip_drm_private *drm_drv = crtc->dev->dev_private; 45 struct psr_drv *psr; 46 unsigned long flags; 47 48 spin_lock_irqsave(&drm_drv->psr_list_lock, flags); 49 list_for_each_entry(psr, &drm_drv->psr_list, list) { 50 if (psr->encoder->crtc == crtc) 51 goto out; 52 } 53 psr = ERR_PTR(-ENODEV); 54 55out: 56 spin_unlock_irqrestore(&drm_drv->psr_list_lock, flags); 57 return psr; 58} 59 60static void psr_set_state_locked(struct psr_drv *psr, enum psr_state state) 61{ 62 /* 63 * Allowed finite state machine: 64 * 65 * PSR_ENABLE < = = = = = > PSR_FLUSH 66 * | ^ | 67 * | | | 68 * v | | 69 * PSR_DISABLE < - - - - - - - - - 70 */ 71 if (state == psr->state || !psr->active) 72 return; 73 74 /* Already disabled in flush, change the state, but not the hardware */ 75 if (state == PSR_DISABLE && psr->state == PSR_FLUSH) { 76 psr->state = state; 77 return; 78 } 79 80 psr->state = state; 81 82 /* Actually commit the state change to hardware */ 83 switch (psr->state) { 84 case PSR_ENABLE: 85 psr->set(psr->encoder, true); 86 break; 87 88 case PSR_DISABLE: 89 case PSR_FLUSH: 90 psr->set(psr->encoder, false); 91 break; 92 } 93} 94 95static void psr_set_state(struct psr_drv *psr, enum psr_state state) 96{ 97 unsigned long flags; 98 99 spin_lock_irqsave(&psr->lock, flags); 100 psr_set_state_locked(psr, state); 101 spin_unlock_irqrestore(&psr->lock, flags); 102} 103 104static void psr_flush_handler(unsigned long data) 105{ 106 struct psr_drv *psr = (struct psr_drv *)data; 107 unsigned long flags; 108 109 /* If the state has changed since we initiated the flush, do nothing */ 110 spin_lock_irqsave(&psr->lock, flags); 111 if (psr->state == PSR_FLUSH) 112 psr_set_state_locked(psr, PSR_ENABLE); 113 spin_unlock_irqrestore(&psr->lock, flags); 114} 115 116/** 117 * rockchip_drm_psr_activate - activate PSR on the given pipe 118 * @crtc: CRTC to obtain the PSR encoder 119 * 120 * Returns: 121 * Zero on success, negative errno on failure. 122 */ 123int rockchip_drm_psr_activate(struct drm_crtc *crtc) 124{ 125 struct psr_drv *psr = find_psr_by_crtc(crtc); 126 unsigned long flags; 127 128 if (IS_ERR(psr)) 129 return PTR_ERR(psr); 130 131 spin_lock_irqsave(&psr->lock, flags); 132 psr->active = true; 133 spin_unlock_irqrestore(&psr->lock, flags); 134 135 return 0; 136} 137EXPORT_SYMBOL(rockchip_drm_psr_activate); 138 139/** 140 * rockchip_drm_psr_deactivate - deactivate PSR on the given pipe 141 * @crtc: CRTC to obtain the PSR encoder 142 * 143 * Returns: 144 * Zero on success, negative errno on failure. 145 */ 146int rockchip_drm_psr_deactivate(struct drm_crtc *crtc) 147{ 148 struct psr_drv *psr = find_psr_by_crtc(crtc); 149 unsigned long flags; 150 151 if (IS_ERR(psr)) 152 return PTR_ERR(psr); 153 154 spin_lock_irqsave(&psr->lock, flags); 155 psr->active = false; 156 spin_unlock_irqrestore(&psr->lock, flags); 157 del_timer_sync(&psr->flush_timer); 158 159 return 0; 160} 161EXPORT_SYMBOL(rockchip_drm_psr_deactivate); 162 163static void rockchip_drm_do_flush(struct psr_drv *psr) 164{ 165 mod_timer(&psr->flush_timer, 166 round_jiffies_up(jiffies + PSR_FLUSH_TIMEOUT)); 167 psr_set_state(psr, PSR_FLUSH); 168} 169 170/** 171 * rockchip_drm_psr_flush - flush a single pipe 172 * @crtc: CRTC of the pipe to flush 173 * 174 * Returns: 175 * 0 on success, -errno on fail 176 */ 177int rockchip_drm_psr_flush(struct drm_crtc *crtc) 178{ 179 struct psr_drv *psr = find_psr_by_crtc(crtc); 180 if (IS_ERR(psr)) 181 return PTR_ERR(psr); 182 183 rockchip_drm_do_flush(psr); 184 return 0; 185} 186EXPORT_SYMBOL(rockchip_drm_psr_flush); 187 188/** 189 * rockchip_drm_psr_flush_all - force to flush all registered PSR encoders 190 * @dev: drm device 191 * 192 * Disable the PSR function for all registered encoders, and then enable the 193 * PSR function back after PSR_FLUSH_TIMEOUT. If encoder PSR state have been 194 * changed during flush time, then keep the state no change after flush 195 * timeout. 196 * 197 * Returns: 198 * Zero on success, negative errno on failure. 199 */ 200void rockchip_drm_psr_flush_all(struct drm_device *dev) 201{ 202 struct rockchip_drm_private *drm_drv = dev->dev_private; 203 struct psr_drv *psr; 204 unsigned long flags; 205 206 spin_lock_irqsave(&drm_drv->psr_list_lock, flags); 207 list_for_each_entry(psr, &drm_drv->psr_list, list) 208 rockchip_drm_do_flush(psr); 209 spin_unlock_irqrestore(&drm_drv->psr_list_lock, flags); 210} 211EXPORT_SYMBOL(rockchip_drm_psr_flush_all); 212 213/** 214 * rockchip_drm_psr_register - register encoder to psr driver 215 * @encoder: encoder that obtain the PSR function 216 * @psr_set: call back to set PSR state 217 * 218 * Returns: 219 * Zero on success, negative errno on failure. 220 */ 221int rockchip_drm_psr_register(struct drm_encoder *encoder, 222 void (*psr_set)(struct drm_encoder *, bool enable)) 223{ 224 struct rockchip_drm_private *drm_drv = encoder->dev->dev_private; 225 struct psr_drv *psr; 226 unsigned long flags; 227 228 if (!encoder || !psr_set) 229 return -EINVAL; 230 231 psr = kzalloc(sizeof(struct psr_drv), GFP_KERNEL); 232 if (!psr) 233 return -ENOMEM; 234 235 setup_timer(&psr->flush_timer, psr_flush_handler, (unsigned long)psr); 236 spin_lock_init(&psr->lock); 237 238 psr->active = true; 239 psr->state = PSR_DISABLE; 240 psr->encoder = encoder; 241 psr->set = psr_set; 242 243 spin_lock_irqsave(&drm_drv->psr_list_lock, flags); 244 list_add_tail(&psr->list, &drm_drv->psr_list); 245 spin_unlock_irqrestore(&drm_drv->psr_list_lock, flags); 246 247 return 0; 248} 249EXPORT_SYMBOL(rockchip_drm_psr_register); 250 251/** 252 * rockchip_drm_psr_unregister - unregister encoder to psr driver 253 * @encoder: encoder that obtain the PSR function 254 * @psr_set: call back to set PSR state 255 * 256 * Returns: 257 * Zero on success, negative errno on failure. 258 */ 259void rockchip_drm_psr_unregister(struct drm_encoder *encoder) 260{ 261 struct rockchip_drm_private *drm_drv = encoder->dev->dev_private; 262 struct psr_drv *psr, *n; 263 unsigned long flags; 264 265 spin_lock_irqsave(&drm_drv->psr_list_lock, flags); 266 list_for_each_entry_safe(psr, n, &drm_drv->psr_list, list) { 267 if (psr->encoder == encoder) { 268 del_timer(&psr->flush_timer); 269 list_del(&psr->list); 270 kfree(psr); 271 } 272 } 273 spin_unlock_irqrestore(&drm_drv->psr_list_lock, flags); 274} 275EXPORT_SYMBOL(rockchip_drm_psr_unregister);