GNU Linux-libre 4.19.207-gnu1
[releases.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_MS    100
22
23 struct psr_drv {
24         struct list_head        list;
25         struct drm_encoder      *encoder;
26
27         struct mutex            lock;
28         int                     inhibit_count;
29         bool                    enabled;
30
31         struct delayed_work     flush_work;
32
33         int (*set)(struct drm_encoder *encoder, bool enable);
34 };
35
36 static struct psr_drv *find_psr_by_encoder(struct drm_encoder *encoder)
37 {
38         struct rockchip_drm_private *drm_drv = encoder->dev->dev_private;
39         struct psr_drv *psr;
40
41         mutex_lock(&drm_drv->psr_list_lock);
42         list_for_each_entry(psr, &drm_drv->psr_list, list) {
43                 if (psr->encoder == encoder)
44                         goto out;
45         }
46         psr = ERR_PTR(-ENODEV);
47
48 out:
49         mutex_unlock(&drm_drv->psr_list_lock);
50         return psr;
51 }
52
53 static int psr_set_state_locked(struct psr_drv *psr, bool enable)
54 {
55         int ret;
56
57         if (psr->inhibit_count > 0)
58                 return -EINVAL;
59
60         if (enable == psr->enabled)
61                 return 0;
62
63         ret = psr->set(psr->encoder, enable);
64         if (ret)
65                 return ret;
66
67         psr->enabled = enable;
68         return 0;
69 }
70
71 static void psr_flush_handler(struct work_struct *work)
72 {
73         struct psr_drv *psr = container_of(to_delayed_work(work),
74                                            struct psr_drv, flush_work);
75
76         mutex_lock(&psr->lock);
77         psr_set_state_locked(psr, true);
78         mutex_unlock(&psr->lock);
79 }
80
81 /**
82  * rockchip_drm_psr_inhibit_put - release PSR inhibit on given encoder
83  * @encoder: encoder to obtain the PSR encoder
84  *
85  * Decrements PSR inhibit count on given encoder. Should be called only
86  * for a PSR inhibit count increment done before. If PSR inhibit counter
87  * reaches zero, PSR flush work is scheduled to make the hardware enter
88  * PSR mode in PSR_FLUSH_TIMEOUT_MS.
89  *
90  * Returns:
91  * Zero on success, negative errno on failure.
92  */
93 int rockchip_drm_psr_inhibit_put(struct drm_encoder *encoder)
94 {
95         struct psr_drv *psr = find_psr_by_encoder(encoder);
96
97         if (IS_ERR(psr))
98                 return PTR_ERR(psr);
99
100         mutex_lock(&psr->lock);
101         --psr->inhibit_count;
102         WARN_ON(psr->inhibit_count < 0);
103         if (!psr->inhibit_count)
104                 mod_delayed_work(system_wq, &psr->flush_work,
105                                  PSR_FLUSH_TIMEOUT_MS);
106         mutex_unlock(&psr->lock);
107
108         return 0;
109 }
110 EXPORT_SYMBOL(rockchip_drm_psr_inhibit_put);
111
112 /**
113  * rockchip_drm_psr_inhibit_get - acquire PSR inhibit on given encoder
114  * @encoder: encoder to obtain the PSR encoder
115  *
116  * Increments PSR inhibit count on given encoder. This function guarantees
117  * that after it returns PSR is turned off on given encoder and no PSR-related
118  * hardware state change occurs at least until a matching call to
119  * rockchip_drm_psr_inhibit_put() is done.
120  *
121  * Returns:
122  * Zero on success, negative errno on failure.
123  */
124 int rockchip_drm_psr_inhibit_get(struct drm_encoder *encoder)
125 {
126         struct psr_drv *psr = find_psr_by_encoder(encoder);
127
128         if (IS_ERR(psr))
129                 return PTR_ERR(psr);
130
131         mutex_lock(&psr->lock);
132         psr_set_state_locked(psr, false);
133         ++psr->inhibit_count;
134         mutex_unlock(&psr->lock);
135         cancel_delayed_work_sync(&psr->flush_work);
136
137         return 0;
138 }
139 EXPORT_SYMBOL(rockchip_drm_psr_inhibit_get);
140
141 static void rockchip_drm_do_flush(struct psr_drv *psr)
142 {
143         cancel_delayed_work_sync(&psr->flush_work);
144
145         mutex_lock(&psr->lock);
146         if (!psr_set_state_locked(psr, false))
147                 mod_delayed_work(system_wq, &psr->flush_work,
148                                  PSR_FLUSH_TIMEOUT_MS);
149         mutex_unlock(&psr->lock);
150 }
151
152 /**
153  * rockchip_drm_psr_flush_all - force to flush all registered PSR encoders
154  * @dev: drm device
155  *
156  * Disable the PSR function for all registered encoders, and then enable the
157  * PSR function back after PSR_FLUSH_TIMEOUT. If encoder PSR state have been
158  * changed during flush time, then keep the state no change after flush
159  * timeout.
160  *
161  * Returns:
162  * Zero on success, negative errno on failure.
163  */
164 void rockchip_drm_psr_flush_all(struct drm_device *dev)
165 {
166         struct rockchip_drm_private *drm_drv = dev->dev_private;
167         struct psr_drv *psr;
168
169         mutex_lock(&drm_drv->psr_list_lock);
170         list_for_each_entry(psr, &drm_drv->psr_list, list)
171                 rockchip_drm_do_flush(psr);
172         mutex_unlock(&drm_drv->psr_list_lock);
173 }
174 EXPORT_SYMBOL(rockchip_drm_psr_flush_all);
175
176 /**
177  * rockchip_drm_psr_register - register encoder to psr driver
178  * @encoder: encoder that obtain the PSR function
179  * @psr_set: call back to set PSR state
180  *
181  * The function returns with PSR inhibit counter initialized with one
182  * and the caller (typically encoder driver) needs to call
183  * rockchip_drm_psr_inhibit_put() when it becomes ready to accept PSR
184  * enable request.
185  *
186  * Returns:
187  * Zero on success, negative errno on failure.
188  */
189 int rockchip_drm_psr_register(struct drm_encoder *encoder,
190                         int (*psr_set)(struct drm_encoder *, bool enable))
191 {
192         struct rockchip_drm_private *drm_drv;
193         struct psr_drv *psr;
194
195         if (!encoder || !psr_set)
196                 return -EINVAL;
197
198         drm_drv = encoder->dev->dev_private;
199
200         psr = kzalloc(sizeof(struct psr_drv), GFP_KERNEL);
201         if (!psr)
202                 return -ENOMEM;
203
204         INIT_DELAYED_WORK(&psr->flush_work, psr_flush_handler);
205         mutex_init(&psr->lock);
206
207         psr->inhibit_count = 1;
208         psr->enabled = false;
209         psr->encoder = encoder;
210         psr->set = psr_set;
211
212         mutex_lock(&drm_drv->psr_list_lock);
213         list_add_tail(&psr->list, &drm_drv->psr_list);
214         mutex_unlock(&drm_drv->psr_list_lock);
215
216         return 0;
217 }
218 EXPORT_SYMBOL(rockchip_drm_psr_register);
219
220 /**
221  * rockchip_drm_psr_unregister - unregister encoder to psr driver
222  * @encoder: encoder that obtain the PSR function
223  * @psr_set: call back to set PSR state
224  *
225  * It is expected that the PSR inhibit counter is 1 when this function is
226  * called, which corresponds to a state when related encoder has been
227  * disconnected from any CRTCs and its driver called
228  * rockchip_drm_psr_inhibit_get() to stop the PSR logic.
229  *
230  * Returns:
231  * Zero on success, negative errno on failure.
232  */
233 void rockchip_drm_psr_unregister(struct drm_encoder *encoder)
234 {
235         struct rockchip_drm_private *drm_drv = encoder->dev->dev_private;
236         struct psr_drv *psr, *n;
237
238         mutex_lock(&drm_drv->psr_list_lock);
239         list_for_each_entry_safe(psr, n, &drm_drv->psr_list, list) {
240                 if (psr->encoder == encoder) {
241                         /*
242                          * Any other value would mean that the encoder
243                          * is still in use.
244                          */
245                         WARN_ON(psr->inhibit_count != 1);
246
247                         list_del(&psr->list);
248                         kfree(psr);
249                 }
250         }
251         mutex_unlock(&drm_drv->psr_list_lock);
252 }
253 EXPORT_SYMBOL(rockchip_drm_psr_unregister);