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