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