1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
4 * Author:Mark Yao <mark.yao@rock-chips.com>
5 *
6 * based on exynos_drm_drv.c
7 */
8
9#include <linux/dma-mapping.h>
10#include <linux/platform_device.h>
11#include <linux/pm_runtime.h>
12#include <linux/module.h>
13#include <linux/of_graph.h>
14#include <linux/of_platform.h>
15#include <linux/component.h>
16#include <linux/console.h>
17#include <linux/iommu.h>
18
19#include <drm/drm_aperture.h>
20#include <drm/drm_drv.h>
21#include <drm/drm_fbdev_generic.h>
22#include <drm/drm_gem_dma_helper.h>
23#include <drm/drm_of.h>
24#include <drm/drm_probe_helper.h>
25#include <drm/drm_vblank.h>
26
27#if defined(CONFIG_ARM_DMA_USE_IOMMU)
28#include <asm/dma-iommu.h>
29#else
30#define arm_iommu_detach_device(...)	({ })
31#define arm_iommu_release_mapping(...)	({ })
32#define to_dma_iommu_mapping(dev) NULL
33#endif
34
35#include "rockchip_drm_drv.h"
36#include "rockchip_drm_fb.h"
37#include "rockchip_drm_gem.h"
38
39#define DRIVER_NAME	"rockchip"
40#define DRIVER_DESC	"RockChip Soc DRM"
41#define DRIVER_DATE	"20140818"
42#define DRIVER_MAJOR	1
43#define DRIVER_MINOR	0
44
45static const struct drm_driver rockchip_drm_driver;
46
47/*
48 * Attach a (component) device to the shared drm dma mapping from master drm
49 * device.  This is used by the VOPs to map GEM buffers to a common DMA
50 * mapping.
51 */
52int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
53				   struct device *dev)
54{
55	struct rockchip_drm_private *private = drm_dev->dev_private;
56	int ret;
57
58	if (!private->domain)
59		return 0;
60
61	if (IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU)) {
62		struct dma_iommu_mapping *mapping = to_dma_iommu_mapping(dev);
63
64		if (mapping) {
65			arm_iommu_detach_device(dev);
66			arm_iommu_release_mapping(mapping);
67		}
68	}
69
70	ret = iommu_attach_device(private->domain, dev);
71	if (ret) {
72		DRM_DEV_ERROR(dev, "Failed to attach iommu device\n");
73		return ret;
74	}
75
76	return 0;
77}
78
79void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
80				    struct device *dev)
81{
82	struct rockchip_drm_private *private = drm_dev->dev_private;
83
84	if (!private->domain)
85		return;
86
87	iommu_detach_device(private->domain, dev);
88}
89
90void rockchip_drm_dma_init_device(struct drm_device *drm_dev,
91				  struct device *dev)
92{
93	struct rockchip_drm_private *private = drm_dev->dev_private;
94
95	if (!device_iommu_mapped(dev))
96		private->iommu_dev = ERR_PTR(-ENODEV);
97	else if (!private->iommu_dev)
98		private->iommu_dev = dev;
99}
100
101static int rockchip_drm_init_iommu(struct drm_device *drm_dev)
102{
103	struct rockchip_drm_private *private = drm_dev->dev_private;
104	struct iommu_domain_geometry *geometry;
105	u64 start, end;
106
107	if (IS_ERR_OR_NULL(private->iommu_dev))
108		return 0;
109
110	private->domain = iommu_domain_alloc(private->iommu_dev->bus);
111	if (!private->domain)
112		return -ENOMEM;
113
114	geometry = &private->domain->geometry;
115	start = geometry->aperture_start;
116	end = geometry->aperture_end;
117
118	DRM_DEBUG("IOMMU context initialized (aperture: %#llx-%#llx)\n",
119		  start, end);
120	drm_mm_init(&private->mm, start, end - start + 1);
121	mutex_init(&private->mm_lock);
122
123	return 0;
124}
125
126static void rockchip_iommu_cleanup(struct drm_device *drm_dev)
127{
128	struct rockchip_drm_private *private = drm_dev->dev_private;
129
130	if (!private->domain)
131		return;
132
133	drm_mm_takedown(&private->mm);
134	iommu_domain_free(private->domain);
135}
136
137static int rockchip_drm_bind(struct device *dev)
138{
139	struct drm_device *drm_dev;
140	struct rockchip_drm_private *private;
141	int ret;
142
143	/* Remove existing drivers that may own the framebuffer memory. */
144	ret = drm_aperture_remove_framebuffers(&rockchip_drm_driver);
145	if (ret) {
146		DRM_DEV_ERROR(dev,
147			      "Failed to remove existing framebuffers - %d.\n",
148			      ret);
149		return ret;
150	}
151
152	drm_dev = drm_dev_alloc(&rockchip_drm_driver, dev);
153	if (IS_ERR(drm_dev))
154		return PTR_ERR(drm_dev);
155
156	dev_set_drvdata(dev, drm_dev);
157
158	private = devm_kzalloc(drm_dev->dev, sizeof(*private), GFP_KERNEL);
159	if (!private) {
160		ret = -ENOMEM;
161		goto err_free;
162	}
163
164	drm_dev->dev_private = private;
165
166	ret = drmm_mode_config_init(drm_dev);
167	if (ret)
168		goto err_free;
169
170	rockchip_drm_mode_config_init(drm_dev);
171
172	/* Try to bind all sub drivers. */
173	ret = component_bind_all(dev, drm_dev);
174	if (ret)
175		goto err_free;
176
177	ret = rockchip_drm_init_iommu(drm_dev);
178	if (ret)
179		goto err_unbind_all;
180
181	ret = drm_vblank_init(drm_dev, drm_dev->mode_config.num_crtc);
182	if (ret)
183		goto err_iommu_cleanup;
184
185	drm_mode_config_reset(drm_dev);
186
187	/* init kms poll for handling hpd */
188	drm_kms_helper_poll_init(drm_dev);
189
190	ret = drm_dev_register(drm_dev, 0);
191	if (ret)
192		goto err_kms_helper_poll_fini;
193
194	drm_fbdev_generic_setup(drm_dev, 0);
195
196	return 0;
197err_kms_helper_poll_fini:
198	drm_kms_helper_poll_fini(drm_dev);
199err_iommu_cleanup:
200	rockchip_iommu_cleanup(drm_dev);
201err_unbind_all:
202	component_unbind_all(dev, drm_dev);
203err_free:
204	drm_dev_put(drm_dev);
205	return ret;
206}
207
208static void rockchip_drm_unbind(struct device *dev)
209{
210	struct drm_device *drm_dev = dev_get_drvdata(dev);
211
212	drm_dev_unregister(drm_dev);
213
214	drm_kms_helper_poll_fini(drm_dev);
215
216	drm_atomic_helper_shutdown(drm_dev);
217	component_unbind_all(dev, drm_dev);
218	rockchip_iommu_cleanup(drm_dev);
219
220	drm_dev_put(drm_dev);
221}
222
223DEFINE_DRM_GEM_FOPS(rockchip_drm_driver_fops);
224
225static const struct drm_driver rockchip_drm_driver = {
226	.driver_features	= DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC,
227	.dumb_create		= rockchip_gem_dumb_create,
228	.gem_prime_import_sg_table	= rockchip_gem_prime_import_sg_table,
229	.fops			= &rockchip_drm_driver_fops,
230	.name	= DRIVER_NAME,
231	.desc	= DRIVER_DESC,
232	.date	= DRIVER_DATE,
233	.major	= DRIVER_MAJOR,
234	.minor	= DRIVER_MINOR,
235};
236
237#ifdef CONFIG_PM_SLEEP
238static int rockchip_drm_sys_suspend(struct device *dev)
239{
240	struct drm_device *drm = dev_get_drvdata(dev);
241
242	return drm_mode_config_helper_suspend(drm);
243}
244
245static int rockchip_drm_sys_resume(struct device *dev)
246{
247	struct drm_device *drm = dev_get_drvdata(dev);
248
249	return drm_mode_config_helper_resume(drm);
250}
251#endif
252
253static const struct dev_pm_ops rockchip_drm_pm_ops = {
254	SET_SYSTEM_SLEEP_PM_OPS(rockchip_drm_sys_suspend,
255				rockchip_drm_sys_resume)
256};
257
258#define MAX_ROCKCHIP_SUB_DRIVERS 16
259static struct platform_driver *rockchip_sub_drivers[MAX_ROCKCHIP_SUB_DRIVERS];
260static int num_rockchip_sub_drivers;
261
262/*
263 * Get the endpoint id of the remote endpoint of the given encoder. This
264 * information is used by the VOP2 driver to identify the encoder.
265 *
266 * @rkencoder: The encoder to get the remote endpoint id from
267 * @np: The encoder device node
268 * @port: The number of the port leading to the VOP2
269 * @reg: The endpoint number leading to the VOP2
270 */
271int rockchip_drm_encoder_set_crtc_endpoint_id(struct rockchip_encoder *rkencoder,
272					      struct device_node *np, int port, int reg)
273{
274	struct of_endpoint ep;
275	struct device_node *en, *ren;
276	int ret;
277
278	en = of_graph_get_endpoint_by_regs(np, port, reg);
279	if (!en)
280		return -ENOENT;
281
282	ren = of_graph_get_remote_endpoint(en);
283	if (!ren)
284		return -ENOENT;
285
286	ret = of_graph_parse_endpoint(ren, &ep);
287	if (ret)
288		return ret;
289
290	rkencoder->crtc_endpoint_id = ep.id;
291
292	return 0;
293}
294
295/*
296 * Check if a vop endpoint is leading to a rockchip subdriver or bridge.
297 * Should be called from the component bind stage of the drivers
298 * to ensure that all subdrivers are probed.
299 *
300 * @ep: endpoint of a rockchip vop
301 *
302 * returns true if subdriver, false if external bridge and -ENODEV
303 * if remote port does not contain a device.
304 */
305int rockchip_drm_endpoint_is_subdriver(struct device_node *ep)
306{
307	struct device_node *node = of_graph_get_remote_port_parent(ep);
308	struct platform_device *pdev;
309	struct device_driver *drv;
310	int i;
311
312	if (!node)
313		return -ENODEV;
314
315	/* status disabled will prevent creation of platform-devices */
316	if (!of_device_is_available(node)) {
317		of_node_put(node);
318		return -ENODEV;
319	}
320
321	pdev = of_find_device_by_node(node);
322	of_node_put(node);
323
324	/* enabled non-platform-devices can immediately return here */
325	if (!pdev)
326		return false;
327
328	/*
329	 * All rockchip subdrivers have probed at this point, so
330	 * any device not having a driver now is an external bridge.
331	 */
332	drv = pdev->dev.driver;
333	if (!drv) {
334		platform_device_put(pdev);
335		return false;
336	}
337
338	for (i = 0; i < num_rockchip_sub_drivers; i++) {
339		if (rockchip_sub_drivers[i] == to_platform_driver(drv)) {
340			platform_device_put(pdev);
341			return true;
342		}
343	}
344
345	platform_device_put(pdev);
346	return false;
347}
348
349static void rockchip_drm_match_remove(struct device *dev)
350{
351	struct device_link *link;
352
353	list_for_each_entry(link, &dev->links.consumers, s_node)
354		device_link_del(link);
355}
356
357static struct component_match *rockchip_drm_match_add(struct device *dev)
358{
359	struct component_match *match = NULL;
360	int i;
361
362	for (i = 0; i < num_rockchip_sub_drivers; i++) {
363		struct platform_driver *drv = rockchip_sub_drivers[i];
364		struct device *p = NULL, *d;
365
366		do {
367			d = platform_find_device_by_driver(p, &drv->driver);
368			put_device(p);
369			p = d;
370
371			if (!d)
372				break;
373
374			device_link_add(dev, d, DL_FLAG_STATELESS);
375			component_match_add(dev, &match, component_compare_dev, d);
376		} while (true);
377	}
378
379	if (IS_ERR(match))
380		rockchip_drm_match_remove(dev);
381
382	return match ?: ERR_PTR(-ENODEV);
383}
384
385static const struct component_master_ops rockchip_drm_ops = {
386	.bind = rockchip_drm_bind,
387	.unbind = rockchip_drm_unbind,
388};
389
390static int rockchip_drm_platform_of_probe(struct device *dev)
391{
392	struct device_node *np = dev->of_node;
393	struct device_node *port;
394	bool found = false;
395	int i;
396
397	if (!np)
398		return -ENODEV;
399
400	for (i = 0;; i++) {
401		port = of_parse_phandle(np, "ports", i);
402		if (!port)
403			break;
404
405		if (!of_device_is_available(port->parent)) {
406			of_node_put(port);
407			continue;
408		}
409
410		found = true;
411		of_node_put(port);
412	}
413
414	if (i == 0) {
415		DRM_DEV_ERROR(dev, "missing 'ports' property\n");
416		return -ENODEV;
417	}
418
419	if (!found) {
420		DRM_DEV_ERROR(dev,
421			      "No available vop found for display-subsystem.\n");
422		return -ENODEV;
423	}
424
425	return 0;
426}
427
428static int rockchip_drm_platform_probe(struct platform_device *pdev)
429{
430	struct device *dev = &pdev->dev;
431	struct component_match *match = NULL;
432	int ret;
433
434	ret = rockchip_drm_platform_of_probe(dev);
435	if (ret)
436		return ret;
437
438	match = rockchip_drm_match_add(dev);
439	if (IS_ERR(match))
440		return PTR_ERR(match);
441
442	ret = component_master_add_with_match(dev, &rockchip_drm_ops, match);
443	if (ret < 0) {
444		rockchip_drm_match_remove(dev);
445		return ret;
446	}
447
448	return 0;
449}
450
451static void rockchip_drm_platform_remove(struct platform_device *pdev)
452{
453	component_master_del(&pdev->dev, &rockchip_drm_ops);
454
455	rockchip_drm_match_remove(&pdev->dev);
456}
457
458static void rockchip_drm_platform_shutdown(struct platform_device *pdev)
459{
460	struct drm_device *drm = platform_get_drvdata(pdev);
461
462	if (drm)
463		drm_atomic_helper_shutdown(drm);
464}
465
466static const struct of_device_id rockchip_drm_dt_ids[] = {
467	{ .compatible = "rockchip,display-subsystem", },
468	{ /* sentinel */ },
469};
470MODULE_DEVICE_TABLE(of, rockchip_drm_dt_ids);
471
472static struct platform_driver rockchip_drm_platform_driver = {
473	.probe = rockchip_drm_platform_probe,
474	.remove_new = rockchip_drm_platform_remove,
475	.shutdown = rockchip_drm_platform_shutdown,
476	.driver = {
477		.name = "rockchip-drm",
478		.of_match_table = rockchip_drm_dt_ids,
479		.pm = &rockchip_drm_pm_ops,
480	},
481};
482
483#define ADD_ROCKCHIP_SUB_DRIVER(drv, cond) { \
484	if (IS_ENABLED(cond) && \
485	    !WARN_ON(num_rockchip_sub_drivers >= MAX_ROCKCHIP_SUB_DRIVERS)) \
486		rockchip_sub_drivers[num_rockchip_sub_drivers++] = &drv; \
487}
488
489static int __init rockchip_drm_init(void)
490{
491	int ret;
492
493	if (drm_firmware_drivers_only())
494		return -ENODEV;
495
496	num_rockchip_sub_drivers = 0;
497	ADD_ROCKCHIP_SUB_DRIVER(vop_platform_driver, CONFIG_ROCKCHIP_VOP);
498	ADD_ROCKCHIP_SUB_DRIVER(vop2_platform_driver, CONFIG_ROCKCHIP_VOP2);
499	ADD_ROCKCHIP_SUB_DRIVER(rockchip_lvds_driver,
500				CONFIG_ROCKCHIP_LVDS);
501	ADD_ROCKCHIP_SUB_DRIVER(rockchip_dp_driver,
502				CONFIG_ROCKCHIP_ANALOGIX_DP);
503	ADD_ROCKCHIP_SUB_DRIVER(cdn_dp_driver, CONFIG_ROCKCHIP_CDN_DP);
504	ADD_ROCKCHIP_SUB_DRIVER(dw_hdmi_rockchip_pltfm_driver,
505				CONFIG_ROCKCHIP_DW_HDMI);
506	ADD_ROCKCHIP_SUB_DRIVER(dw_mipi_dsi_rockchip_driver,
507				CONFIG_ROCKCHIP_DW_MIPI_DSI);
508	ADD_ROCKCHIP_SUB_DRIVER(inno_hdmi_driver, CONFIG_ROCKCHIP_INNO_HDMI);
509	ADD_ROCKCHIP_SUB_DRIVER(rk3066_hdmi_driver,
510				CONFIG_ROCKCHIP_RK3066_HDMI);
511
512	ret = platform_register_drivers(rockchip_sub_drivers,
513					num_rockchip_sub_drivers);
514	if (ret)
515		return ret;
516
517	ret = platform_driver_register(&rockchip_drm_platform_driver);
518	if (ret)
519		goto err_unreg_drivers;
520
521	return 0;
522
523err_unreg_drivers:
524	platform_unregister_drivers(rockchip_sub_drivers,
525				    num_rockchip_sub_drivers);
526	return ret;
527}
528
529static void __exit rockchip_drm_fini(void)
530{
531	platform_driver_unregister(&rockchip_drm_platform_driver);
532
533	platform_unregister_drivers(rockchip_sub_drivers,
534				    num_rockchip_sub_drivers);
535}
536
537module_init(rockchip_drm_init);
538module_exit(rockchip_drm_fini);
539
540MODULE_AUTHOR("Mark Yao <mark.yao@rock-chips.com>");
541MODULE_DESCRIPTION("ROCKCHIP DRM Driver");
542MODULE_LICENSE("GPL v2");
543