xref: /OK3568_Linux_fs/kernel/drivers/gpu/drm/imx/dcss/dcss-dev.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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