xref: /linux/drivers/gpu/drm/imx/dc/dc-drv.c (revision 88b8c6ae2ccb3ef9dbb04c8e13a4d1a98c42e922)
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright 2024 NXP
4  */
5 
6 #include <linux/clk.h>
7 #include <linux/component.h>
8 #include <linux/device.h>
9 #include <linux/dma-mapping.h>
10 #include <linux/module.h>
11 #include <linux/of.h>
12 #include <linux/of_platform.h>
13 #include <linux/platform_device.h>
14 #include <linux/pm.h>
15 #include <linux/pm_runtime.h>
16 
17 #include <drm/clients/drm_client_setup.h>
18 #include <drm/drm_atomic_helper.h>
19 #include <drm/drm_drv.h>
20 #include <drm/drm_fbdev_dma.h>
21 #include <drm/drm_fourcc.h>
22 #include <drm/drm_gem_dma_helper.h>
23 #include <drm/drm_managed.h>
24 #include <drm/drm_modeset_helper.h>
25 #include <drm/drm_of.h>
26 
27 #include "dc-de.h"
28 #include "dc-drv.h"
29 #include "dc-pe.h"
30 
31 struct dc_priv {
32 	struct drm_device *drm;
33 	struct clk *clk_cfg;
34 };
35 
36 DEFINE_DRM_GEM_DMA_FOPS(dc_drm_driver_fops);
37 
38 static struct drm_driver dc_drm_driver = {
39 	.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC,
40 	DRM_GEM_DMA_DRIVER_OPS,
41 	DRM_FBDEV_DMA_DRIVER_OPS,
42 	.fops = &dc_drm_driver_fops,
43 	.name = "imx8-dc",
44 	.desc = "i.MX8 DC DRM graphics",
45 	.major = 1,
46 	.minor = 0,
47 	.patchlevel = 0,
48 };
49 
50 static void
51 dc_add_components(struct device *dev, struct component_match **matchptr)
52 {
53 	struct device_node *child, *grandchild;
54 
55 	for_each_available_child_of_node(dev->of_node, child) {
56 		/* The interrupt controller is not a component. */
57 		if (of_device_is_compatible(child, "fsl,imx8qxp-dc-intc"))
58 			continue;
59 
60 		drm_of_component_match_add(dev, matchptr, component_compare_of,
61 					   child);
62 
63 		for_each_available_child_of_node(child, grandchild)
64 			drm_of_component_match_add(dev, matchptr,
65 						   component_compare_of,
66 						   grandchild);
67 	}
68 }
69 
70 static int dc_drm_component_bind_all(struct dc_drm_device *dc_drm)
71 {
72 	struct drm_device *drm = &dc_drm->base;
73 	int ret;
74 
75 	ret = component_bind_all(drm->dev, dc_drm);
76 	if (ret)
77 		return ret;
78 
79 	dc_de_post_bind(dc_drm);
80 	dc_pe_post_bind(dc_drm);
81 
82 	return 0;
83 }
84 
85 static void dc_drm_component_unbind_all(void *ptr)
86 {
87 	struct dc_drm_device *dc_drm = ptr;
88 	struct drm_device *drm = &dc_drm->base;
89 
90 	component_unbind_all(drm->dev, dc_drm);
91 }
92 
93 static int dc_drm_bind(struct device *dev)
94 {
95 	struct dc_priv *priv = dev_get_drvdata(dev);
96 	struct dc_drm_device *dc_drm;
97 	struct drm_device *drm;
98 	int ret;
99 
100 	dc_drm = devm_drm_dev_alloc(dev, &dc_drm_driver, struct dc_drm_device,
101 				    base);
102 	if (IS_ERR(dc_drm))
103 		return PTR_ERR(dc_drm);
104 
105 	drm = &dc_drm->base;
106 
107 	ret = dc_drm_component_bind_all(dc_drm);
108 	if (ret)
109 		return ret;
110 
111 	ret = devm_add_action_or_reset(dev, dc_drm_component_unbind_all,
112 				       dc_drm);
113 	if (ret)
114 		return ret;
115 
116 	ret = dc_kms_init(dc_drm);
117 	if (ret)
118 		return ret;
119 
120 	ret = drm_dev_register(drm, 0);
121 	if (ret) {
122 		dev_err(dev, "failed to register drm device: %d\n", ret);
123 		goto err;
124 	}
125 
126 	drm_client_setup_with_fourcc(drm, DRM_FORMAT_XRGB8888);
127 
128 	priv->drm = drm;
129 
130 	return 0;
131 
132 err:
133 	dc_kms_uninit(dc_drm);
134 
135 	return ret;
136 }
137 
138 static void dc_drm_unbind(struct device *dev)
139 {
140 	struct dc_priv *priv = dev_get_drvdata(dev);
141 	struct dc_drm_device *dc_drm = to_dc_drm_device(priv->drm);
142 	struct drm_device *drm = &dc_drm->base;
143 
144 	priv->drm = NULL;
145 	drm_dev_unplug(drm);
146 	dc_kms_uninit(dc_drm);
147 	drm_atomic_helper_shutdown(drm);
148 }
149 
150 static const struct component_master_ops dc_drm_ops = {
151 	.bind = dc_drm_bind,
152 	.unbind = dc_drm_unbind,
153 };
154 
155 static int dc_probe(struct platform_device *pdev)
156 {
157 	struct component_match *match = NULL;
158 	struct dc_priv *priv;
159 	int ret;
160 
161 	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
162 	if (!priv)
163 		return -ENOMEM;
164 
165 	priv->clk_cfg = devm_clk_get(&pdev->dev, NULL);
166 	if (IS_ERR(priv->clk_cfg))
167 		return dev_err_probe(&pdev->dev, PTR_ERR(priv->clk_cfg),
168 				     "failed to get cfg clock\n");
169 
170 	dev_set_drvdata(&pdev->dev, priv);
171 
172 	ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
173 	if (ret)
174 		return ret;
175 
176 	ret = devm_pm_runtime_enable(&pdev->dev);
177 	if (ret)
178 		return ret;
179 
180 	ret = devm_of_platform_populate(&pdev->dev);
181 	if (ret)
182 		return ret;
183 
184 	dc_add_components(&pdev->dev, &match);
185 
186 	ret = component_master_add_with_match(&pdev->dev, &dc_drm_ops, match);
187 	if (ret)
188 		return dev_err_probe(&pdev->dev, ret,
189 				     "failed to add component master\n");
190 
191 	return 0;
192 }
193 
194 static void dc_remove(struct platform_device *pdev)
195 {
196 	component_master_del(&pdev->dev, &dc_drm_ops);
197 }
198 
199 static int dc_runtime_suspend(struct device *dev)
200 {
201 	struct dc_priv *priv = dev_get_drvdata(dev);
202 
203 	clk_disable_unprepare(priv->clk_cfg);
204 
205 	return 0;
206 }
207 
208 static int dc_runtime_resume(struct device *dev)
209 {
210 	struct dc_priv *priv = dev_get_drvdata(dev);
211 	int ret;
212 
213 	ret = clk_prepare_enable(priv->clk_cfg);
214 	if (ret)
215 		dev_err(dev, "failed to enable cfg clock: %d\n", ret);
216 
217 	return ret;
218 }
219 
220 static int dc_suspend(struct device *dev)
221 {
222 	struct dc_priv *priv = dev_get_drvdata(dev);
223 
224 	return drm_mode_config_helper_suspend(priv->drm);
225 }
226 
227 static int dc_resume(struct device *dev)
228 {
229 	struct dc_priv *priv = dev_get_drvdata(dev);
230 
231 	return drm_mode_config_helper_resume(priv->drm);
232 }
233 
234 static void dc_shutdown(struct platform_device *pdev)
235 {
236 	struct dc_priv *priv = dev_get_drvdata(&pdev->dev);
237 
238 	drm_atomic_helper_shutdown(priv->drm);
239 }
240 
241 static const struct dev_pm_ops dc_pm_ops = {
242 	RUNTIME_PM_OPS(dc_runtime_suspend, dc_runtime_resume, NULL)
243 	SYSTEM_SLEEP_PM_OPS(dc_suspend, dc_resume)
244 };
245 
246 static const struct of_device_id dc_dt_ids[] = {
247 	{ .compatible = "fsl,imx8qxp-dc", },
248 	{ /* sentinel */ }
249 };
250 MODULE_DEVICE_TABLE(of, dc_dt_ids);
251 
252 static struct platform_driver dc_driver = {
253 	.probe = dc_probe,
254 	.remove = dc_remove,
255 	.shutdown = dc_shutdown,
256 	.driver = {
257 		.name = "imx8-dc",
258 		.of_match_table	= dc_dt_ids,
259 		.pm = pm_sleep_ptr(&dc_pm_ops),
260 	},
261 };
262 
263 static struct platform_driver * const dc_drivers[] = {
264 	&dc_cf_driver,
265 	&dc_de_driver,
266 	&dc_ed_driver,
267 	&dc_fg_driver,
268 	&dc_fl_driver,
269 	&dc_fw_driver,
270 	&dc_ic_driver,
271 	&dc_lb_driver,
272 	&dc_pe_driver,
273 	&dc_tc_driver,
274 	&dc_driver,
275 };
276 
277 static int __init dc_drm_init(void)
278 {
279 	return platform_register_drivers(dc_drivers, ARRAY_SIZE(dc_drivers));
280 }
281 
282 static void __exit dc_drm_exit(void)
283 {
284 	platform_unregister_drivers(dc_drivers, ARRAY_SIZE(dc_drivers));
285 }
286 
287 module_init(dc_drm_init);
288 module_exit(dc_drm_exit);
289 
290 MODULE_DESCRIPTION("i.MX8 Display Controller DRM Driver");
291 MODULE_AUTHOR("Liu Ying <victor.liu@nxp.com>");
292 MODULE_LICENSE("GPL");
293