• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
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 
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 
find_psr_by_crtc(struct drm_crtc * crtc)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;
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 
55 out:
56 	spin_unlock_irqrestore(&drm_drv->psr_list_lock, flags);
57 	return psr;
58 }
59 
psr_set_state_locked(struct psr_drv * psr,enum psr_state state)60 static 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 
psr_set_state(struct psr_drv * psr,enum psr_state state)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 
psr_flush_handler(unsigned long data)104 static 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  */
rockchip_drm_psr_activate(struct drm_crtc * crtc)123 int 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 }
137 EXPORT_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  */
rockchip_drm_psr_deactivate(struct drm_crtc * crtc)146 int 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 }
161 EXPORT_SYMBOL(rockchip_drm_psr_deactivate);
162 
rockchip_drm_do_flush(struct psr_drv * psr)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 }
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  */
rockchip_drm_psr_flush(struct drm_crtc * crtc)177 int 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 }
186 EXPORT_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  */
rockchip_drm_psr_flush_all(struct drm_device * dev)200 void 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 }
211 EXPORT_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  */
rockchip_drm_psr_register(struct drm_encoder * encoder,void (* psr_set)(struct drm_encoder *,bool enable))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;
225 	struct psr_drv *psr;
226 	unsigned long flags;
227 
228 	if (!encoder || !psr_set)
229 		return -EINVAL;
230 
231 	drm_drv = encoder->dev->dev_private;
232 
233 	psr = kzalloc(sizeof(struct psr_drv), GFP_KERNEL);
234 	if (!psr)
235 		return -ENOMEM;
236 
237 	setup_timer(&psr->flush_timer, psr_flush_handler, (unsigned long)psr);
238 	spin_lock_init(&psr->lock);
239 
240 	psr->active = true;
241 	psr->state = PSR_DISABLE;
242 	psr->encoder = encoder;
243 	psr->set = psr_set;
244 
245 	spin_lock_irqsave(&drm_drv->psr_list_lock, flags);
246 	list_add_tail(&psr->list, &drm_drv->psr_list);
247 	spin_unlock_irqrestore(&drm_drv->psr_list_lock, flags);
248 
249 	return 0;
250 }
251 EXPORT_SYMBOL(rockchip_drm_psr_register);
252 
253 /**
254  * rockchip_drm_psr_unregister - unregister encoder to psr driver
255  * @encoder: encoder that obtain the PSR function
256  * @psr_set: call back to set PSR state
257  *
258  * Returns:
259  * Zero on success, negative errno on failure.
260  */
rockchip_drm_psr_unregister(struct drm_encoder * encoder)261 void rockchip_drm_psr_unregister(struct drm_encoder *encoder)
262 {
263 	struct rockchip_drm_private *drm_drv = encoder->dev->dev_private;
264 	struct psr_drv *psr, *n;
265 	unsigned long flags;
266 
267 	spin_lock_irqsave(&drm_drv->psr_list_lock, flags);
268 	list_for_each_entry_safe(psr, n, &drm_drv->psr_list, list) {
269 		if (psr->encoder == encoder) {
270 			del_timer(&psr->flush_timer);
271 			list_del(&psr->list);
272 			kfree(psr);
273 		}
274 	}
275 	spin_unlock_irqrestore(&drm_drv->psr_list_lock, flags);
276 }
277 EXPORT_SYMBOL(rockchip_drm_psr_unregister);
278