Merge tag 'amd-drm-next-6.3-2023-01-13' of https://gitlab.freedesktop.org/agd5f/linux...
[linux-2.6-microblaze.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 static int dcss_dev_suspend(struct device *dev)
253 {
254         struct dcss_dev *dcss = dcss_drv_dev_to_dcss(dev);
255         struct drm_device *ddev = dcss_drv_dev_to_drm(dev);
256         struct dcss_kms_dev *kms = container_of(ddev, struct dcss_kms_dev, base);
257         int ret;
258
259         drm_bridge_connector_disable_hpd(kms->connector);
260
261         drm_mode_config_helper_suspend(ddev);
262
263         if (pm_runtime_suspended(dev))
264                 return 0;
265
266         ret = dcss_ctxld_suspend(dcss->ctxld);
267         if (ret)
268                 return ret;
269
270         dcss_clocks_disable(dcss);
271
272         return 0;
273 }
274
275 static int dcss_dev_resume(struct device *dev)
276 {
277         struct dcss_dev *dcss = dcss_drv_dev_to_dcss(dev);
278         struct drm_device *ddev = dcss_drv_dev_to_drm(dev);
279         struct dcss_kms_dev *kms = container_of(ddev, struct dcss_kms_dev, base);
280
281         if (pm_runtime_suspended(dev)) {
282                 drm_mode_config_helper_resume(ddev);
283                 return 0;
284         }
285
286         dcss_clocks_enable(dcss);
287
288         dcss_blkctl_cfg(dcss->blkctl);
289
290         dcss_ctxld_resume(dcss->ctxld);
291
292         drm_mode_config_helper_resume(ddev);
293
294         drm_bridge_connector_enable_hpd(kms->connector);
295
296         return 0;
297 }
298
299 static int dcss_dev_runtime_suspend(struct device *dev)
300 {
301         struct dcss_dev *dcss = dcss_drv_dev_to_dcss(dev);
302         int ret;
303
304         ret = dcss_ctxld_suspend(dcss->ctxld);
305         if (ret)
306                 return ret;
307
308         dcss_clocks_disable(dcss);
309
310         return 0;
311 }
312
313 static int dcss_dev_runtime_resume(struct device *dev)
314 {
315         struct dcss_dev *dcss = dcss_drv_dev_to_dcss(dev);
316
317         dcss_clocks_enable(dcss);
318
319         dcss_blkctl_cfg(dcss->blkctl);
320
321         dcss_ctxld_resume(dcss->ctxld);
322
323         return 0;
324 }
325
326 EXPORT_GPL_DEV_PM_OPS(dcss_dev_pm_ops) = {
327         RUNTIME_PM_OPS(dcss_dev_runtime_suspend, dcss_dev_runtime_resume, NULL)
328         SYSTEM_SLEEP_PM_OPS(dcss_dev_suspend, dcss_dev_resume)
329 };