]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blob - drivers/gpu/drm/rockchip/rockchip_drm_psr.c
Merge branch 'for-next' of git://people.freedesktop.org/~seanpaul/dogwood into drm...
[mirror_ubuntu-bionic-kernel.git] / drivers / gpu / drm / rockchip / rockchip_drm_psr.c
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(3000) /* 3 seconds */
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 enum psr_state state;
35
36 struct timer_list flush_timer;
37
38 void (*set)(struct drm_encoder *encoder, bool enable);
39 };
40
41 static struct psr_drv *find_psr_by_crtc(struct drm_crtc *crtc)
42 {
43 struct rockchip_drm_private *drm_drv = crtc->dev->dev_private;
44 struct psr_drv *psr;
45 unsigned long flags;
46
47 spin_lock_irqsave(&drm_drv->psr_list_lock, flags);
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
54 out:
55 spin_unlock_irqrestore(&drm_drv->psr_list_lock, flags);
56 return psr;
57 }
58
59 static void psr_set_state_locked(struct psr_drv *psr, enum psr_state state)
60 {
61 /*
62 * Allowed finite state machine:
63 *
64 * PSR_ENABLE < = = = = = > PSR_FLUSH
65 * | ^ |
66 * | | |
67 * v | |
68 * PSR_DISABLE < - - - - - - - - -
69 */
70 if (state == psr->state)
71 return;
72
73 /* Requesting a flush when disabled is a noop */
74 if (state == PSR_FLUSH && psr->state == PSR_DISABLE)
75 return;
76
77 psr->state = state;
78
79 /* Already disabled in flush, change the state, but not the hardware */
80 if (state == PSR_DISABLE && psr->state == PSR_FLUSH)
81 return;
82
83 /* Actually commit the state change to hardware */
84 switch (psr->state) {
85 case PSR_ENABLE:
86 psr->set(psr->encoder, true);
87 break;
88
89 case PSR_DISABLE:
90 case PSR_FLUSH:
91 psr->set(psr->encoder, false);
92 break;
93 }
94 }
95
96 static void psr_set_state(struct psr_drv *psr, enum psr_state state)
97 {
98 unsigned long flags;
99
100 spin_lock_irqsave(&psr->lock, flags);
101 psr_set_state_locked(psr, state);
102 spin_unlock_irqrestore(&psr->lock, flags);
103 }
104
105 static void psr_flush_handler(unsigned long data)
106 {
107 struct psr_drv *psr = (struct psr_drv *)data;
108 unsigned long flags;
109
110 /* If the state has changed since we initiated the flush, do nothing */
111 spin_lock_irqsave(&psr->lock, flags);
112 if (psr->state == PSR_FLUSH)
113 psr_set_state_locked(psr, PSR_ENABLE);
114 spin_unlock_irqrestore(&psr->lock, flags);
115 }
116
117 /**
118 * rockchip_drm_psr_enable - enable the encoder PSR which bind to given CRTC
119 * @crtc: CRTC to obtain the PSR encoder
120 *
121 * Returns:
122 * Zero on success, negative errno on failure.
123 */
124 int rockchip_drm_psr_enable(struct drm_crtc *crtc)
125 {
126 struct psr_drv *psr = find_psr_by_crtc(crtc);
127
128 if (IS_ERR(psr))
129 return PTR_ERR(psr);
130
131 psr_set_state(psr, PSR_ENABLE);
132 return 0;
133 }
134 EXPORT_SYMBOL(rockchip_drm_psr_enable);
135
136 /**
137 * rockchip_drm_psr_disable - disable the encoder PSR which bind to given CRTC
138 * @crtc: CRTC to obtain the PSR encoder
139 *
140 * Returns:
141 * Zero on success, negative errno on failure.
142 */
143 int rockchip_drm_psr_disable(struct drm_crtc *crtc)
144 {
145 struct psr_drv *psr = find_psr_by_crtc(crtc);
146
147 if (IS_ERR(psr))
148 return PTR_ERR(psr);
149
150 psr_set_state(psr, PSR_DISABLE);
151 return 0;
152 }
153 EXPORT_SYMBOL(rockchip_drm_psr_disable);
154
155 /**
156 * rockchip_drm_psr_flush - force to flush all registered PSR encoders
157 * @dev: drm device
158 *
159 * Disable the PSR function for all registered encoders, and then enable the
160 * PSR function back after PSR_FLUSH_TIMEOUT. If encoder PSR state have been
161 * changed during flush time, then keep the state no change after flush
162 * timeout.
163 *
164 * Returns:
165 * Zero on success, negative errno on failure.
166 */
167 void rockchip_drm_psr_flush(struct drm_device *dev)
168 {
169 struct rockchip_drm_private *drm_drv = dev->dev_private;
170 struct psr_drv *psr;
171 unsigned long flags;
172
173 spin_lock_irqsave(&drm_drv->psr_list_lock, flags);
174 list_for_each_entry(psr, &drm_drv->psr_list, list) {
175 mod_timer(&psr->flush_timer,
176 round_jiffies_up(jiffies + PSR_FLUSH_TIMEOUT));
177
178 psr_set_state(psr, PSR_FLUSH);
179 }
180 spin_unlock_irqrestore(&drm_drv->psr_list_lock, flags);
181 }
182 EXPORT_SYMBOL(rockchip_drm_psr_flush);
183
184 /**
185 * rockchip_drm_psr_register - register encoder to psr driver
186 * @encoder: encoder that obtain the PSR function
187 * @psr_set: call back to set PSR state
188 *
189 * Returns:
190 * Zero on success, negative errno on failure.
191 */
192 int rockchip_drm_psr_register(struct drm_encoder *encoder,
193 void (*psr_set)(struct drm_encoder *, bool enable))
194 {
195 struct rockchip_drm_private *drm_drv = encoder->dev->dev_private;
196 struct psr_drv *psr;
197 unsigned long flags;
198
199 if (!encoder || !psr_set)
200 return -EINVAL;
201
202 psr = kzalloc(sizeof(struct psr_drv), GFP_KERNEL);
203 if (!psr)
204 return -ENOMEM;
205
206 setup_timer(&psr->flush_timer, psr_flush_handler, (unsigned long)psr);
207 spin_lock_init(&psr->lock);
208
209 psr->state = PSR_DISABLE;
210 psr->encoder = encoder;
211 psr->set = psr_set;
212
213 spin_lock_irqsave(&drm_drv->psr_list_lock, flags);
214 list_add_tail(&psr->list, &drm_drv->psr_list);
215 spin_unlock_irqrestore(&drm_drv->psr_list_lock, flags);
216
217 return 0;
218 }
219 EXPORT_SYMBOL(rockchip_drm_psr_register);
220
221 /**
222 * rockchip_drm_psr_unregister - unregister encoder to psr driver
223 * @encoder: encoder that obtain the PSR function
224 * @psr_set: call back to set PSR state
225 *
226 * Returns:
227 * Zero on success, negative errno on failure.
228 */
229 void rockchip_drm_psr_unregister(struct drm_encoder *encoder)
230 {
231 struct rockchip_drm_private *drm_drv = encoder->dev->dev_private;
232 struct psr_drv *psr, *n;
233 unsigned long flags;
234
235 spin_lock_irqsave(&drm_drv->psr_list_lock, flags);
236 list_for_each_entry_safe(psr, n, &drm_drv->psr_list, list) {
237 if (psr->encoder == encoder) {
238 del_timer(&psr->flush_timer);
239 list_del(&psr->list);
240 kfree(psr);
241 }
242 }
243 spin_unlock_irqrestore(&drm_drv->psr_list_lock, flags);
244 }
245 EXPORT_SYMBOL(rockchip_drm_psr_unregister);