]>
Commit | Line | Data |
---|---|---|
5182c1a5 YY |
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 | ||
604bac48 | 21 | #define PSR_FLUSH_TIMEOUT msecs_to_jiffies(100) |
5182c1a5 YY |
22 | |
23 | enum psr_state { | |
24 | PSR_FLUSH, | |
25 | PSR_ENABLE, | |
26 | PSR_DISABLE, | |
27 | }; | |
28 | ||
29 | struct psr_drv { | |
30 | struct list_head list; | |
31 | struct drm_encoder *encoder; | |
32 | ||
eec85347 | 33 | spinlock_t lock; |
b883c9ba | 34 | bool active; |
5182c1a5 YY |
35 | enum psr_state state; |
36 | ||
5182c1a5 YY |
37 | struct timer_list flush_timer; |
38 | ||
39 | void (*set)(struct drm_encoder *encoder, bool enable); | |
40 | }; | |
41 | ||
42 | static 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; | |
18d8d4d2 | 46 | unsigned long flags; |
5182c1a5 | 47 | |
18d8d4d2 SP |
48 | spin_lock_irqsave(&drm_drv->psr_list_lock, flags); |
49 | list_for_each_entry(psr, &drm_drv->psr_list, list) { | |
5182c1a5 | 50 | if (psr->encoder->crtc == crtc) |
18d8d4d2 SP |
51 | goto out; |
52 | } | |
53 | psr = ERR_PTR(-ENODEV); | |
5182c1a5 | 54 | |
18d8d4d2 SP |
55 | out: |
56 | spin_unlock_irqrestore(&drm_drv->psr_list_lock, flags); | |
57 | return psr; | |
5182c1a5 YY |
58 | } |
59 | ||
eec85347 | 60 | static void psr_set_state_locked(struct psr_drv *psr, enum psr_state state) |
5182c1a5 | 61 | { |
5182c1a5 YY |
62 | /* |
63 | * Allowed finite state machine: | |
64 | * | |
65 | * PSR_ENABLE < = = = = = > PSR_FLUSH | |
46bdc649 SP |
66 | * | ^ | |
67 | * | | | | |
68 | * v | | | |
5182c1a5 YY |
69 | * PSR_DISABLE < - - - - - - - - - |
70 | */ | |
b883c9ba | 71 | if (state == psr->state || !psr->active) |
5182c1a5 YY |
72 | return; |
73 | ||
46bdc649 | 74 | /* Already disabled in flush, change the state, but not the hardware */ |
be91a983 SP |
75 | if (state == PSR_DISABLE && psr->state == PSR_FLUSH) { |
76 | psr->state = state; | |
5182c1a5 | 77 | return; |
be91a983 SP |
78 | } |
79 | ||
80 | psr->state = state; | |
5182c1a5 | 81 | |
46bdc649 | 82 | /* Actually commit the state change to hardware */ |
23c0f3dc | 83 | switch (psr->state) { |
5182c1a5 YY |
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 | ||
eec85347 SP |
95 | static 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 | ||
e99e88a9 | 104 | static void psr_flush_handler(struct timer_list *t) |
5182c1a5 | 105 | { |
e99e88a9 | 106 | struct psr_drv *psr = from_timer(psr, t, flush_timer); |
eec85347 | 107 | unsigned long flags; |
5182c1a5 | 108 | |
46bdc649 | 109 | /* If the state has changed since we initiated the flush, do nothing */ |
eec85347 SP |
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); | |
5182c1a5 YY |
114 | } |
115 | ||
116 | /** | |
b883c9ba | 117 | * rockchip_drm_psr_activate - activate PSR on the given pipe |
5182c1a5 YY |
118 | * @crtc: CRTC to obtain the PSR encoder |
119 | * | |
120 | * Returns: | |
121 | * Zero on success, negative errno on failure. | |
122 | */ | |
b883c9ba | 123 | int rockchip_drm_psr_activate(struct drm_crtc *crtc) |
5182c1a5 YY |
124 | { |
125 | struct psr_drv *psr = find_psr_by_crtc(crtc); | |
b883c9ba | 126 | unsigned long flags; |
5182c1a5 YY |
127 | |
128 | if (IS_ERR(psr)) | |
129 | return PTR_ERR(psr); | |
130 | ||
b883c9ba SP |
131 | spin_lock_irqsave(&psr->lock, flags); |
132 | psr->active = true; | |
133 | spin_unlock_irqrestore(&psr->lock, flags); | |
134 | ||
5182c1a5 YY |
135 | return 0; |
136 | } | |
b883c9ba | 137 | EXPORT_SYMBOL(rockchip_drm_psr_activate); |
5182c1a5 YY |
138 | |
139 | /** | |
b883c9ba | 140 | * rockchip_drm_psr_deactivate - deactivate PSR on the given pipe |
5182c1a5 YY |
141 | * @crtc: CRTC to obtain the PSR encoder |
142 | * | |
143 | * Returns: | |
144 | * Zero on success, negative errno on failure. | |
145 | */ | |
b883c9ba | 146 | int rockchip_drm_psr_deactivate(struct drm_crtc *crtc) |
5182c1a5 YY |
147 | { |
148 | struct psr_drv *psr = find_psr_by_crtc(crtc); | |
b883c9ba SP |
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 | } | |
161 | EXPORT_SYMBOL(rockchip_drm_psr_deactivate); | |
162 | ||
163 | static 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 | } | |
5182c1a5 | 169 | |
b883c9ba SP |
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 | */ | |
177 | int rockchip_drm_psr_flush(struct drm_crtc *crtc) | |
178 | { | |
179 | struct psr_drv *psr = find_psr_by_crtc(crtc); | |
5182c1a5 YY |
180 | if (IS_ERR(psr)) |
181 | return PTR_ERR(psr); | |
182 | ||
b883c9ba | 183 | rockchip_drm_do_flush(psr); |
5182c1a5 YY |
184 | return 0; |
185 | } | |
b883c9ba | 186 | EXPORT_SYMBOL(rockchip_drm_psr_flush); |
5182c1a5 YY |
187 | |
188 | /** | |
b883c9ba | 189 | * rockchip_drm_psr_flush_all - force to flush all registered PSR encoders |
5182c1a5 YY |
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 | */ | |
b883c9ba | 200 | void rockchip_drm_psr_flush_all(struct drm_device *dev) |
5182c1a5 YY |
201 | { |
202 | struct rockchip_drm_private *drm_drv = dev->dev_private; | |
203 | struct psr_drv *psr; | |
18d8d4d2 | 204 | unsigned long flags; |
5182c1a5 | 205 | |
18d8d4d2 | 206 | spin_lock_irqsave(&drm_drv->psr_list_lock, flags); |
b883c9ba SP |
207 | list_for_each_entry(psr, &drm_drv->psr_list, list) |
208 | rockchip_drm_do_flush(psr); | |
18d8d4d2 | 209 | spin_unlock_irqrestore(&drm_drv->psr_list_lock, flags); |
5182c1a5 | 210 | } |
b883c9ba | 211 | EXPORT_SYMBOL(rockchip_drm_psr_flush_all); |
5182c1a5 YY |
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 | */ | |
221 | int 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; | |
18d8d4d2 | 226 | unsigned long flags; |
5182c1a5 YY |
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 | ||
e99e88a9 | 235 | timer_setup(&psr->flush_timer, psr_flush_handler, 0); |
eec85347 | 236 | spin_lock_init(&psr->lock); |
5182c1a5 | 237 | |
b883c9ba | 238 | psr->active = true; |
5182c1a5 YY |
239 | psr->state = PSR_DISABLE; |
240 | psr->encoder = encoder; | |
241 | psr->set = psr_set; | |
242 | ||
18d8d4d2 | 243 | spin_lock_irqsave(&drm_drv->psr_list_lock, flags); |
5182c1a5 | 244 | list_add_tail(&psr->list, &drm_drv->psr_list); |
18d8d4d2 | 245 | spin_unlock_irqrestore(&drm_drv->psr_list_lock, flags); |
5182c1a5 YY |
246 | |
247 | return 0; | |
248 | } | |
249 | EXPORT_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 | */ | |
259 | void 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; | |
18d8d4d2 | 263 | unsigned long flags; |
5182c1a5 | 264 | |
18d8d4d2 | 265 | spin_lock_irqsave(&drm_drv->psr_list_lock, flags); |
5182c1a5 YY |
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 | } | |
18d8d4d2 | 273 | spin_unlock_irqrestore(&drm_drv->psr_list_lock, flags); |
5182c1a5 YY |
274 | } |
275 | EXPORT_SYMBOL(rockchip_drm_psr_unregister); |