Merge tag 'erofs-for-5.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/xiang...
[sfrench/cifs-2.6.git] / drivers / gpu / drm / imx / dcss / dcss-dev.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright 2019 NXP.
4  */
5
6 #include <linux/clk.h>
7 #include <linux/of_device.h>
8 #include <linux/of_graph.h>
9 #include <linux/pm_runtime.h>
10 #include <linux/slab.h>
11 #include <drm/drm_bridge_connector.h>
12 #include <drm/drm_device.h>
13 #include <drm/drm_modeset_helper.h>
14
15 #include "dcss-dev.h"
16 #include "dcss-kms.h"
17
18 static void dcss_clocks_enable(struct dcss_dev *dcss)
19 {
20         clk_prepare_enable(dcss->axi_clk);
21         clk_prepare_enable(dcss->apb_clk);
22         clk_prepare_enable(dcss->rtrm_clk);
23         clk_prepare_enable(dcss->dtrc_clk);
24         clk_prepare_enable(dcss->pix_clk);
25 }
26
27 static void dcss_clocks_disable(struct dcss_dev *dcss)
28 {
29         clk_disable_unprepare(dcss->pix_clk);
30         clk_disable_unprepare(dcss->dtrc_clk);
31         clk_disable_unprepare(dcss->rtrm_clk);
32         clk_disable_unprepare(dcss->apb_clk);
33         clk_disable_unprepare(dcss->axi_clk);
34 }
35
36 static void dcss_disable_dtg_and_ss_cb(void *data)
37 {
38         struct dcss_dev *dcss = data;
39
40         dcss->disable_callback = NULL;
41
42         dcss_ss_shutoff(dcss->ss);
43         dcss_dtg_shutoff(dcss->dtg);
44
45         complete(&dcss->disable_completion);
46 }
47
48 void dcss_disable_dtg_and_ss(struct dcss_dev *dcss)
49 {
50         dcss->disable_callback = dcss_disable_dtg_and_ss_cb;
51 }
52
53 void dcss_enable_dtg_and_ss(struct dcss_dev *dcss)
54 {
55         if (dcss->disable_callback)
56                 dcss->disable_callback = NULL;
57
58         dcss_dtg_enable(dcss->dtg);
59         dcss_ss_enable(dcss->ss);
60 }
61
62 static int dcss_submodules_init(struct dcss_dev *dcss)
63 {
64         int ret = 0;
65         u32 base_addr = dcss->start_addr;
66         const struct dcss_type_data *devtype = dcss->devtype;
67
68         dcss_clocks_enable(dcss);
69
70         ret = dcss_blkctl_init(dcss, base_addr + devtype->blkctl_ofs);
71         if (ret)
72                 return ret;
73
74         ret = dcss_ctxld_init(dcss, base_addr + devtype->ctxld_ofs);
75         if (ret)
76                 goto ctxld_err;
77
78         ret = dcss_dtg_init(dcss, base_addr + devtype->dtg_ofs);
79         if (ret)
80                 goto dtg_err;
81
82         ret = dcss_ss_init(dcss, base_addr + devtype->ss_ofs);
83         if (ret)
84                 goto ss_err;
85
86         ret = dcss_dpr_init(dcss, base_addr + devtype->dpr_ofs);
87         if (ret)
88                 goto dpr_err;
89
90         ret = dcss_scaler_init(dcss, base_addr + devtype->scaler_ofs);
91         if (ret)
92                 goto scaler_err;
93
94         dcss_clocks_disable(dcss);
95
96         return 0;
97
98 scaler_err:
99         dcss_dpr_exit(dcss->dpr);
100
101 dpr_err:
102         dcss_ss_exit(dcss->ss);
103
104 ss_err:
105         dcss_dtg_exit(dcss->dtg);
106
107 dtg_err:
108         dcss_ctxld_exit(dcss->ctxld);
109
110 ctxld_err:
111         dcss_blkctl_exit(dcss->blkctl);
112
113         dcss_clocks_disable(dcss);
114
115         return ret;
116 }
117
118 static void dcss_submodules_stop(struct dcss_dev *dcss)
119 {
120         dcss_clocks_enable(dcss);
121         dcss_scaler_exit(dcss->scaler);
122         dcss_dpr_exit(dcss->dpr);
123         dcss_ss_exit(dcss->ss);
124         dcss_dtg_exit(dcss->dtg);
125         dcss_ctxld_exit(dcss->ctxld);
126         dcss_blkctl_exit(dcss->blkctl);
127         dcss_clocks_disable(dcss);
128 }
129
130 static int dcss_clks_init(struct dcss_dev *dcss)
131 {
132         int i;
133         struct {
134                 const char *id;
135                 struct clk **clk;
136         } clks[] = {
137                 {"apb",   &dcss->apb_clk},
138                 {"axi",   &dcss->axi_clk},
139                 {"pix",   &dcss->pix_clk},
140                 {"rtrm",  &dcss->rtrm_clk},
141                 {"dtrc",  &dcss->dtrc_clk},
142         };
143
144         for (i = 0; i < ARRAY_SIZE(clks); i++) {
145                 *clks[i].clk = devm_clk_get(dcss->dev, clks[i].id);
146                 if (IS_ERR(*clks[i].clk)) {
147                         dev_err(dcss->dev, "failed to get %s clock\n",
148                                 clks[i].id);
149                         return PTR_ERR(*clks[i].clk);
150                 }
151         }
152
153         return 0;
154 }
155
156 static void dcss_clks_release(struct dcss_dev *dcss)
157 {
158         devm_clk_put(dcss->dev, dcss->dtrc_clk);
159         devm_clk_put(dcss->dev, dcss->rtrm_clk);
160         devm_clk_put(dcss->dev, dcss->pix_clk);
161         devm_clk_put(dcss->dev, dcss->axi_clk);
162         devm_clk_put(dcss->dev, dcss->apb_clk);
163 }
164
165 struct dcss_dev *dcss_dev_create(struct device *dev, bool hdmi_output)
166 {
167         struct platform_device *pdev = to_platform_device(dev);
168         int ret;
169         struct resource *res;
170         struct dcss_dev *dcss;
171         const struct dcss_type_data *devtype;
172
173         devtype = of_device_get_match_data(dev);
174         if (!devtype) {
175                 dev_err(dev, "no device match found\n");
176                 return ERR_PTR(-ENODEV);
177         }
178
179         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
180         if (!res) {
181                 dev_err(dev, "cannot get memory resource\n");
182                 return ERR_PTR(-EINVAL);
183         }
184
185         dcss = kzalloc(sizeof(*dcss), GFP_KERNEL);
186         if (!dcss)
187                 return ERR_PTR(-ENOMEM);
188
189         dcss->dev = dev;
190         dcss->devtype = devtype;
191         dcss->hdmi_output = hdmi_output;
192
193         ret = dcss_clks_init(dcss);
194         if (ret) {
195                 dev_err(dev, "clocks initialization failed\n");
196                 goto err;
197         }
198
199         dcss->of_port = of_graph_get_port_by_id(dev->of_node, 0);
200         if (!dcss->of_port) {
201                 dev_err(dev, "no port@0 node in %s\n", dev->of_node->full_name);
202                 ret = -ENODEV;
203                 goto clks_err;
204         }
205
206         dcss->start_addr = res->start;
207
208         ret = dcss_submodules_init(dcss);
209         if (ret) {
210                 of_node_put(dcss->of_port);
211                 dev_err(dev, "submodules initialization failed\n");
212                 goto clks_err;
213         }
214
215         init_completion(&dcss->disable_completion);
216
217         pm_runtime_set_autosuspend_delay(dev, 100);
218         pm_runtime_use_autosuspend(dev);
219         pm_runtime_set_suspended(dev);
220         pm_runtime_allow(dev);
221         pm_runtime_enable(dev);
222
223         return dcss;
224
225 clks_err:
226         dcss_clks_release(dcss);
227
228 err:
229         kfree(dcss);
230
231         return ERR_PTR(ret);
232 }
233
234 void dcss_dev_destroy(struct dcss_dev *dcss)
235 {
236         if (!pm_runtime_suspended(dcss->dev)) {
237                 dcss_ctxld_suspend(dcss->ctxld);
238                 dcss_clocks_disable(dcss);
239         }
240
241         of_node_put(dcss->of_port);
242
243         pm_runtime_disable(dcss->dev);
244
245         dcss_submodules_stop(dcss);
246
247         dcss_clks_release(dcss);
248
249         kfree(dcss);
250 }
251
252 #ifdef CONFIG_PM_SLEEP
253 int dcss_dev_suspend(struct device *dev)
254 {
255         struct dcss_dev *dcss = dcss_drv_dev_to_dcss(dev);
256         struct drm_device *ddev = dcss_drv_dev_to_drm(dev);
257         struct dcss_kms_dev *kms = container_of(ddev, struct dcss_kms_dev, base);
258         int ret;
259
260         drm_bridge_connector_disable_hpd(kms->connector);
261
262         drm_mode_config_helper_suspend(ddev);
263
264         if (pm_runtime_suspended(dev))
265                 return 0;
266
267         ret = dcss_ctxld_suspend(dcss->ctxld);
268         if (ret)
269                 return ret;
270
271         dcss_clocks_disable(dcss);
272
273         return 0;
274 }
275
276 int dcss_dev_resume(struct device *dev)
277 {
278         struct dcss_dev *dcss = dcss_drv_dev_to_dcss(dev);
279         struct drm_device *ddev = dcss_drv_dev_to_drm(dev);
280         struct dcss_kms_dev *kms = container_of(ddev, struct dcss_kms_dev, base);
281
282         if (pm_runtime_suspended(dev)) {
283                 drm_mode_config_helper_resume(ddev);
284                 return 0;
285         }
286
287         dcss_clocks_enable(dcss);
288
289         dcss_blkctl_cfg(dcss->blkctl);
290
291         dcss_ctxld_resume(dcss->ctxld);
292
293         drm_mode_config_helper_resume(ddev);
294
295         drm_bridge_connector_enable_hpd(kms->connector);
296
297         return 0;
298 }
299 #endif /* CONFIG_PM_SLEEP */
300
301 #ifdef CONFIG_PM
302 int dcss_dev_runtime_suspend(struct device *dev)
303 {
304         struct dcss_dev *dcss = dcss_drv_dev_to_dcss(dev);
305         int ret;
306
307         ret = dcss_ctxld_suspend(dcss->ctxld);
308         if (ret)
309                 return ret;
310
311         dcss_clocks_disable(dcss);
312
313         return 0;
314 }
315
316 int dcss_dev_runtime_resume(struct device *dev)
317 {
318         struct dcss_dev *dcss = dcss_drv_dev_to_dcss(dev);
319
320         dcss_clocks_enable(dcss);
321
322         dcss_blkctl_cfg(dcss->blkctl);
323
324         dcss_ctxld_resume(dcss->ctxld);
325
326         return 0;
327 }
328 #endif /* CONFIG_PM */