1d76271d2SHyun Kwon // SPDX-License-Identifier: GPL-2.0
2d76271d2SHyun Kwon /*
3d76271d2SHyun Kwon  * ZynqMP DisplayPort Subsystem Driver
4d76271d2SHyun Kwon  *
5d76271d2SHyun Kwon  * Copyright (C) 2017 - 2020 Xilinx, Inc.
6d76271d2SHyun Kwon  *
7d76271d2SHyun Kwon  * Authors:
8d76271d2SHyun Kwon  * - Hyun Woo Kwon <hyun.kwon@xilinx.com>
9d76271d2SHyun Kwon  * - Laurent Pinchart <laurent.pinchart@ideasonboard.com>
10d76271d2SHyun Kwon  */
11d76271d2SHyun Kwon 
12d76271d2SHyun Kwon #include <linux/clk.h>
13d76271d2SHyun Kwon #include <linux/dma-mapping.h>
14d76271d2SHyun Kwon #include <linux/module.h>
1552c2cf14SLaurent Pinchart #include <linux/of_graph.h>
16d76271d2SHyun Kwon #include <linux/of_reserved_mem.h>
17d76271d2SHyun Kwon #include <linux/platform_device.h>
18d76271d2SHyun Kwon #include <linux/pm_runtime.h>
196ca91bb4SLaurent Pinchart #include <linux/slab.h>
20d76271d2SHyun Kwon 
21d76271d2SHyun Kwon #include <drm/drm_atomic_helper.h>
2256167161SLaurent Pinchart #include <drm/drm_bridge.h>
23074ef0ceSLaurent Pinchart #include <drm/drm_modeset_helper.h>
24fad54534SJavier Martinez Canillas #include <drm/drm_module.h>
25d76271d2SHyun Kwon 
26d76271d2SHyun Kwon #include "zynqmp_disp.h"
27d76271d2SHyun Kwon #include "zynqmp_dp.h"
28d76271d2SHyun Kwon #include "zynqmp_dpsub.h"
2976c8eeb7SLaurent Pinchart #include "zynqmp_kms.h"
30d76271d2SHyun Kwon 
31d76271d2SHyun Kwon /* -----------------------------------------------------------------------------
32d76271d2SHyun Kwon  * Power Management
33d76271d2SHyun Kwon  */
34d76271d2SHyun Kwon 
zynqmp_dpsub_suspend(struct device * dev)35d76271d2SHyun Kwon static int __maybe_unused zynqmp_dpsub_suspend(struct device *dev)
36d76271d2SHyun Kwon {
37d76271d2SHyun Kwon 	struct zynqmp_dpsub *dpsub = dev_get_drvdata(dev);
38d76271d2SHyun Kwon 
39d189835fSLaurent Pinchart 	if (!dpsub->drm)
40d189835fSLaurent Pinchart 		return 0;
41d189835fSLaurent Pinchart 
42d189835fSLaurent Pinchart 	return drm_mode_config_helper_suspend(&dpsub->drm->dev);
43d76271d2SHyun Kwon }
44d76271d2SHyun Kwon 
zynqmp_dpsub_resume(struct device * dev)45d76271d2SHyun Kwon static int __maybe_unused zynqmp_dpsub_resume(struct device *dev)
46d76271d2SHyun Kwon {
47d76271d2SHyun Kwon 	struct zynqmp_dpsub *dpsub = dev_get_drvdata(dev);
48d76271d2SHyun Kwon 
49d189835fSLaurent Pinchart 	if (!dpsub->drm)
50d189835fSLaurent Pinchart 		return 0;
51d189835fSLaurent Pinchart 
52d189835fSLaurent Pinchart 	return drm_mode_config_helper_resume(&dpsub->drm->dev);
53d76271d2SHyun Kwon }
54d76271d2SHyun Kwon 
55d76271d2SHyun Kwon static const struct dev_pm_ops zynqmp_dpsub_pm_ops = {
56d76271d2SHyun Kwon 	SET_SYSTEM_SLEEP_PM_OPS(zynqmp_dpsub_suspend, zynqmp_dpsub_resume)
57d76271d2SHyun Kwon };
58d76271d2SHyun Kwon 
59d76271d2SHyun Kwon /* -----------------------------------------------------------------------------
60c979296eSLaurent Pinchart  * DPSUB Configuration
61c979296eSLaurent Pinchart  */
62c979296eSLaurent Pinchart 
63c979296eSLaurent Pinchart /**
64c979296eSLaurent Pinchart  * zynqmp_dpsub_audio_enabled - If the audio is enabled
65c979296eSLaurent Pinchart  * @dpsub: DisplayPort subsystem
66c979296eSLaurent Pinchart  *
67c979296eSLaurent Pinchart  * Return if the audio is enabled depending on the audio clock.
68c979296eSLaurent Pinchart  *
69c979296eSLaurent Pinchart  * Return: true if audio is enabled, or false.
70c979296eSLaurent Pinchart  */
zynqmp_dpsub_audio_enabled(struct zynqmp_dpsub * dpsub)71c979296eSLaurent Pinchart bool zynqmp_dpsub_audio_enabled(struct zynqmp_dpsub *dpsub)
72c979296eSLaurent Pinchart {
73c979296eSLaurent Pinchart 	return !!dpsub->aud_clk;
74c979296eSLaurent Pinchart }
75c979296eSLaurent Pinchart 
76c979296eSLaurent Pinchart /**
77c979296eSLaurent Pinchart  * zynqmp_dpsub_get_audio_clk_rate - Get the current audio clock rate
78c979296eSLaurent Pinchart  * @dpsub: DisplayPort subsystem
79c979296eSLaurent Pinchart  *
80c979296eSLaurent Pinchart  * Return: the current audio clock rate.
81c979296eSLaurent Pinchart  */
zynqmp_dpsub_get_audio_clk_rate(struct zynqmp_dpsub * dpsub)82c979296eSLaurent Pinchart unsigned int zynqmp_dpsub_get_audio_clk_rate(struct zynqmp_dpsub *dpsub)
83c979296eSLaurent Pinchart {
84c979296eSLaurent Pinchart 	if (zynqmp_dpsub_audio_enabled(dpsub))
85c979296eSLaurent Pinchart 		return 0;
86c979296eSLaurent Pinchart 	return clk_get_rate(dpsub->aud_clk);
87c979296eSLaurent Pinchart }
88c979296eSLaurent Pinchart 
89c979296eSLaurent Pinchart /* -----------------------------------------------------------------------------
90d76271d2SHyun Kwon  * Probe & Remove
91d76271d2SHyun Kwon  */
92d76271d2SHyun Kwon 
zynqmp_dpsub_init_clocks(struct zynqmp_dpsub * dpsub)93d76271d2SHyun Kwon static int zynqmp_dpsub_init_clocks(struct zynqmp_dpsub *dpsub)
94d76271d2SHyun Kwon {
95d76271d2SHyun Kwon 	int ret;
96d76271d2SHyun Kwon 
97d76271d2SHyun Kwon 	dpsub->apb_clk = devm_clk_get(dpsub->dev, "dp_apb_clk");
98d76271d2SHyun Kwon 	if (IS_ERR(dpsub->apb_clk))
99d76271d2SHyun Kwon 		return PTR_ERR(dpsub->apb_clk);
100d76271d2SHyun Kwon 
101d76271d2SHyun Kwon 	ret = clk_prepare_enable(dpsub->apb_clk);
102d76271d2SHyun Kwon 	if (ret) {
103d76271d2SHyun Kwon 		dev_err(dpsub->dev, "failed to enable the APB clock\n");
104d76271d2SHyun Kwon 		return ret;
105d76271d2SHyun Kwon 	}
106d76271d2SHyun Kwon 
107c979296eSLaurent Pinchart 	/*
108c979296eSLaurent Pinchart 	 * Try the live PL video clock, and fall back to the PS clock if the
109c979296eSLaurent Pinchart 	 * live PL video clock isn't valid.
110c979296eSLaurent Pinchart 	 */
1111682ade6SLaurent Pinchart 	dpsub->vid_clk = devm_clk_get(dpsub->dev, "dp_live_video_in_clk");
1121682ade6SLaurent Pinchart 	if (!IS_ERR(dpsub->vid_clk))
1131682ade6SLaurent Pinchart 		dpsub->vid_clk_from_ps = false;
1141682ade6SLaurent Pinchart 	else if (PTR_ERR(dpsub->vid_clk) == -EPROBE_DEFER)
1151682ade6SLaurent Pinchart 		return PTR_ERR(dpsub->vid_clk);
1161682ade6SLaurent Pinchart 
1171682ade6SLaurent Pinchart 	if (IS_ERR_OR_NULL(dpsub->vid_clk)) {
1181682ade6SLaurent Pinchart 		dpsub->vid_clk = devm_clk_get(dpsub->dev, "dp_vtc_pixel_clk_in");
1191682ade6SLaurent Pinchart 		if (IS_ERR(dpsub->vid_clk)) {
1201682ade6SLaurent Pinchart 			dev_err(dpsub->dev, "failed to init any video clock\n");
1211682ade6SLaurent Pinchart 			return PTR_ERR(dpsub->vid_clk);
1221682ade6SLaurent Pinchart 		}
1231682ade6SLaurent Pinchart 		dpsub->vid_clk_from_ps = true;
1241682ade6SLaurent Pinchart 	}
1251682ade6SLaurent Pinchart 
126c979296eSLaurent Pinchart 	/*
127c979296eSLaurent Pinchart 	 * Try the live PL audio clock, and fall back to the PS clock if the
128c979296eSLaurent Pinchart 	 * live PL audio clock isn't valid. Missing audio clock disables audio
129c979296eSLaurent Pinchart 	 * but isn't an error.
130c979296eSLaurent Pinchart 	 */
131c979296eSLaurent Pinchart 	dpsub->aud_clk = devm_clk_get(dpsub->dev, "dp_live_audio_aclk");
132c979296eSLaurent Pinchart 	if (!IS_ERR(dpsub->aud_clk)) {
133c979296eSLaurent Pinchart 		dpsub->aud_clk_from_ps = false;
134c979296eSLaurent Pinchart 		return 0;
135c979296eSLaurent Pinchart 	}
136c979296eSLaurent Pinchart 
137c979296eSLaurent Pinchart 	dpsub->aud_clk = devm_clk_get(dpsub->dev, "dp_aud_clk");
138c979296eSLaurent Pinchart 	if (!IS_ERR(dpsub->aud_clk)) {
139c979296eSLaurent Pinchart 		dpsub->aud_clk_from_ps = true;
140c979296eSLaurent Pinchart 		return 0;
141c979296eSLaurent Pinchart 	}
142c979296eSLaurent Pinchart 
143c979296eSLaurent Pinchart 	dev_info(dpsub->dev, "audio disabled due to missing clock\n");
144d76271d2SHyun Kwon 	return 0;
145d76271d2SHyun Kwon }
146d76271d2SHyun Kwon 
zynqmp_dpsub_parse_dt(struct zynqmp_dpsub * dpsub)14752c2cf14SLaurent Pinchart static int zynqmp_dpsub_parse_dt(struct zynqmp_dpsub *dpsub)
14852c2cf14SLaurent Pinchart {
14952c2cf14SLaurent Pinchart 	struct device_node *np;
15052c2cf14SLaurent Pinchart 	unsigned int i;
15152c2cf14SLaurent Pinchart 
15252c2cf14SLaurent Pinchart 	/*
15352c2cf14SLaurent Pinchart 	 * For backward compatibility with old device trees that don't contain
15452c2cf14SLaurent Pinchart 	 * ports, consider that only the DP output port is connected if no
15552c2cf14SLaurent Pinchart 	 * ports child no exists.
15652c2cf14SLaurent Pinchart 	 */
15752c2cf14SLaurent Pinchart 	np = of_get_child_by_name(dpsub->dev->of_node, "ports");
15852c2cf14SLaurent Pinchart 	of_node_put(np);
15952c2cf14SLaurent Pinchart 	if (!np) {
16052c2cf14SLaurent Pinchart 		dev_warn(dpsub->dev, "missing ports, update DT bindings\n");
16152c2cf14SLaurent Pinchart 		dpsub->connected_ports = BIT(ZYNQMP_DPSUB_PORT_OUT_DP);
16251ae3bd4SLaurent Pinchart 		dpsub->dma_enabled = true;
16352c2cf14SLaurent Pinchart 		return 0;
16452c2cf14SLaurent Pinchart 	}
16552c2cf14SLaurent Pinchart 
16652c2cf14SLaurent Pinchart 	/* Check which ports are connected. */
16752c2cf14SLaurent Pinchart 	for (i = 0; i < ZYNQMP_DPSUB_NUM_PORTS; ++i) {
16852c2cf14SLaurent Pinchart 		struct device_node *np;
16952c2cf14SLaurent Pinchart 
17052c2cf14SLaurent Pinchart 		np = of_graph_get_remote_node(dpsub->dev->of_node, i, -1);
17152c2cf14SLaurent Pinchart 		if (np) {
17252c2cf14SLaurent Pinchart 			dpsub->connected_ports |= BIT(i);
17352c2cf14SLaurent Pinchart 			of_node_put(np);
17452c2cf14SLaurent Pinchart 		}
17552c2cf14SLaurent Pinchart 	}
17652c2cf14SLaurent Pinchart 
17752c2cf14SLaurent Pinchart 	/* Sanity checks. */
17856167161SLaurent Pinchart 	if ((dpsub->connected_ports & BIT(ZYNQMP_DPSUB_PORT_LIVE_VIDEO)) &&
17956167161SLaurent Pinchart 	    (dpsub->connected_ports & BIT(ZYNQMP_DPSUB_PORT_LIVE_GFX))) {
18056167161SLaurent Pinchart 		dev_err(dpsub->dev, "only one live video input is supported\n");
18156167161SLaurent Pinchart 		return -EINVAL;
18256167161SLaurent Pinchart 	}
18352c2cf14SLaurent Pinchart 
18456167161SLaurent Pinchart 	if ((dpsub->connected_ports & BIT(ZYNQMP_DPSUB_PORT_LIVE_VIDEO)) ||
18556167161SLaurent Pinchart 	    (dpsub->connected_ports & BIT(ZYNQMP_DPSUB_PORT_LIVE_GFX))) {
18656167161SLaurent Pinchart 		if (dpsub->vid_clk_from_ps) {
18756167161SLaurent Pinchart 			dev_err(dpsub->dev,
18856167161SLaurent Pinchart 				"live video input requires PL clock\n");
18956167161SLaurent Pinchart 			return -EINVAL;
19056167161SLaurent Pinchart 		}
19156167161SLaurent Pinchart 	} else {
19251ae3bd4SLaurent Pinchart 		dpsub->dma_enabled = true;
19356167161SLaurent Pinchart 	}
19451ae3bd4SLaurent Pinchart 
19552c2cf14SLaurent Pinchart 	if (dpsub->connected_ports & BIT(ZYNQMP_DPSUB_PORT_LIVE_AUDIO))
19652c2cf14SLaurent Pinchart 		dev_warn(dpsub->dev, "live audio unsupported, ignoring\n");
19752c2cf14SLaurent Pinchart 
19852c2cf14SLaurent Pinchart 	if ((dpsub->connected_ports & BIT(ZYNQMP_DPSUB_PORT_OUT_VIDEO)) ||
19952c2cf14SLaurent Pinchart 	    (dpsub->connected_ports & BIT(ZYNQMP_DPSUB_PORT_OUT_AUDIO)))
20052c2cf14SLaurent Pinchart 		dev_warn(dpsub->dev, "output to PL unsupported, ignoring\n");
20152c2cf14SLaurent Pinchart 
20252c2cf14SLaurent Pinchart 	if (!(dpsub->connected_ports & BIT(ZYNQMP_DPSUB_PORT_OUT_DP))) {
20352c2cf14SLaurent Pinchart 		dev_err(dpsub->dev, "DP output port not connected\n");
20452c2cf14SLaurent Pinchart 		return -EINVAL;
20552c2cf14SLaurent Pinchart 	}
20652c2cf14SLaurent Pinchart 
20752c2cf14SLaurent Pinchart 	return 0;
20852c2cf14SLaurent Pinchart }
20952c2cf14SLaurent Pinchart 
zynqmp_dpsub_release(struct zynqmp_dpsub * dpsub)210d189835fSLaurent Pinchart void zynqmp_dpsub_release(struct zynqmp_dpsub *dpsub)
2116ca91bb4SLaurent Pinchart {
2126ca91bb4SLaurent Pinchart 	kfree(dpsub->disp);
2136ca91bb4SLaurent Pinchart 	kfree(dpsub->dp);
214d189835fSLaurent Pinchart 	kfree(dpsub);
2156ca91bb4SLaurent Pinchart }
2166ca91bb4SLaurent Pinchart 
zynqmp_dpsub_probe(struct platform_device * pdev)217d76271d2SHyun Kwon static int zynqmp_dpsub_probe(struct platform_device *pdev)
218d76271d2SHyun Kwon {
219d76271d2SHyun Kwon 	struct zynqmp_dpsub *dpsub;
220d76271d2SHyun Kwon 	int ret;
221d76271d2SHyun Kwon 
222d76271d2SHyun Kwon 	/* Allocate private data. */
223d189835fSLaurent Pinchart 	dpsub = kzalloc(sizeof(*dpsub), GFP_KERNEL);
224d189835fSLaurent Pinchart 	if (!dpsub)
225d189835fSLaurent Pinchart 		return -ENOMEM;
2266ca91bb4SLaurent Pinchart 
227d76271d2SHyun Kwon 	dpsub->dev = &pdev->dev;
228d76271d2SHyun Kwon 	platform_set_drvdata(pdev, dpsub);
229d76271d2SHyun Kwon 
2301832fba7SJiasheng Jiang 	ret = dma_set_mask(dpsub->dev, DMA_BIT_MASK(ZYNQMP_DISP_MAX_DMA_BIT));
2311832fba7SJiasheng Jiang 	if (ret)
2321832fba7SJiasheng Jiang 		return ret;
233d76271d2SHyun Kwon 
234d76271d2SHyun Kwon 	/* Try the reserved memory. Proceed if there's none. */
235d76271d2SHyun Kwon 	of_reserved_mem_device_init(&pdev->dev);
236d76271d2SHyun Kwon 
237d76271d2SHyun Kwon 	ret = zynqmp_dpsub_init_clocks(dpsub);
238d76271d2SHyun Kwon 	if (ret < 0)
239d76271d2SHyun Kwon 		goto err_mem;
240d76271d2SHyun Kwon 
24152c2cf14SLaurent Pinchart 	ret = zynqmp_dpsub_parse_dt(dpsub);
24252c2cf14SLaurent Pinchart 	if (ret < 0)
24352c2cf14SLaurent Pinchart 		goto err_mem;
24452c2cf14SLaurent Pinchart 
245d76271d2SHyun Kwon 	pm_runtime_enable(&pdev->dev);
246d76271d2SHyun Kwon 
247d76271d2SHyun Kwon 	/*
248d76271d2SHyun Kwon 	 * DP should be probed first so that the zynqmp_disp can set the output
249d76271d2SHyun Kwon 	 * format accordingly.
250d76271d2SHyun Kwon 	 */
2516ca91bb4SLaurent Pinchart 	ret = zynqmp_dp_probe(dpsub);
252d76271d2SHyun Kwon 	if (ret)
253d76271d2SHyun Kwon 		goto err_pm;
254d76271d2SHyun Kwon 
2556ca91bb4SLaurent Pinchart 	ret = zynqmp_disp_probe(dpsub);
256d76271d2SHyun Kwon 	if (ret)
257d76271d2SHyun Kwon 		goto err_dp;
258d76271d2SHyun Kwon 
2596ead3eccSSean Anderson 	drm_bridge_add(dpsub->bridge);
2606ead3eccSSean Anderson 
26156167161SLaurent Pinchart 	if (dpsub->dma_enabled) {
262d76271d2SHyun Kwon 		ret = zynqmp_dpsub_drm_init(dpsub);
263d76271d2SHyun Kwon 		if (ret)
264d76271d2SHyun Kwon 			goto err_disp;
26556167161SLaurent Pinchart 	}
266d76271d2SHyun Kwon 
267d76271d2SHyun Kwon 	dev_info(&pdev->dev, "ZynqMP DisplayPort Subsystem driver probed");
268d76271d2SHyun Kwon 
269d76271d2SHyun Kwon 	return 0;
270d76271d2SHyun Kwon 
271d76271d2SHyun Kwon err_disp:
272d76271d2SHyun Kwon 	zynqmp_disp_remove(dpsub);
273d76271d2SHyun Kwon err_dp:
274d76271d2SHyun Kwon 	zynqmp_dp_remove(dpsub);
275d76271d2SHyun Kwon err_pm:
276d76271d2SHyun Kwon 	pm_runtime_disable(&pdev->dev);
277d76271d2SHyun Kwon 	clk_disable_unprepare(dpsub->apb_clk);
278d76271d2SHyun Kwon err_mem:
279d76271d2SHyun Kwon 	of_reserved_mem_device_release(&pdev->dev);
280d189835fSLaurent Pinchart 	if (!dpsub->drm)
281d189835fSLaurent Pinchart 		zynqmp_dpsub_release(dpsub);
282d76271d2SHyun Kwon 	return ret;
283d76271d2SHyun Kwon }
284d76271d2SHyun Kwon 
zynqmp_dpsub_remove(struct platform_device * pdev)28571722685SUwe Kleine-König static void zynqmp_dpsub_remove(struct platform_device *pdev)
286d76271d2SHyun Kwon {
287d76271d2SHyun Kwon 	struct zynqmp_dpsub *dpsub = platform_get_drvdata(pdev);
288d76271d2SHyun Kwon 
289d189835fSLaurent Pinchart 	if (dpsub->drm)
290074ef0ceSLaurent Pinchart 		zynqmp_dpsub_drm_cleanup(dpsub);
291d76271d2SHyun Kwon 
2926ead3eccSSean Anderson 	drm_bridge_remove(dpsub->bridge);
293d76271d2SHyun Kwon 	zynqmp_disp_remove(dpsub);
294d76271d2SHyun Kwon 	zynqmp_dp_remove(dpsub);
295d76271d2SHyun Kwon 
296d76271d2SHyun Kwon 	pm_runtime_disable(&pdev->dev);
297d76271d2SHyun Kwon 	clk_disable_unprepare(dpsub->apb_clk);
298d76271d2SHyun Kwon 	of_reserved_mem_device_release(&pdev->dev);
299d76271d2SHyun Kwon 
300d189835fSLaurent Pinchart 	if (!dpsub->drm)
301d189835fSLaurent Pinchart 		zynqmp_dpsub_release(dpsub);
302d76271d2SHyun Kwon }
303d76271d2SHyun Kwon 
zynqmp_dpsub_shutdown(struct platform_device * pdev)304d76271d2SHyun Kwon static void zynqmp_dpsub_shutdown(struct platform_device *pdev)
305d76271d2SHyun Kwon {
306d76271d2SHyun Kwon 	struct zynqmp_dpsub *dpsub = platform_get_drvdata(pdev);
307d76271d2SHyun Kwon 
308d189835fSLaurent Pinchart 	if (!dpsub->drm)
309d189835fSLaurent Pinchart 		return;
310d189835fSLaurent Pinchart 
311d189835fSLaurent Pinchart 	drm_atomic_helper_shutdown(&dpsub->drm->dev);
312d76271d2SHyun Kwon }
313d76271d2SHyun Kwon 
314d76271d2SHyun Kwon static const struct of_device_id zynqmp_dpsub_of_match[] = {
315d76271d2SHyun Kwon 	{ .compatible = "xlnx,zynqmp-dpsub-1.7", },
316d76271d2SHyun Kwon 	{ /* end of table */ },
317d76271d2SHyun Kwon };
318d76271d2SHyun Kwon MODULE_DEVICE_TABLE(of, zynqmp_dpsub_of_match);
319d76271d2SHyun Kwon 
320d76271d2SHyun Kwon static struct platform_driver zynqmp_dpsub_driver = {
321d76271d2SHyun Kwon 	.probe			= zynqmp_dpsub_probe,
32271722685SUwe Kleine-König 	.remove_new		= zynqmp_dpsub_remove,
323d76271d2SHyun Kwon 	.shutdown		= zynqmp_dpsub_shutdown,
324d76271d2SHyun Kwon 	.driver			= {
325d76271d2SHyun Kwon 		.name		= "zynqmp-dpsub",
326d76271d2SHyun Kwon 		.pm		= &zynqmp_dpsub_pm_ops,
327d76271d2SHyun Kwon 		.of_match_table	= zynqmp_dpsub_of_match,
328d76271d2SHyun Kwon 	},
329d76271d2SHyun Kwon };
330d76271d2SHyun Kwon 
331fad54534SJavier Martinez Canillas drm_module_platform_driver(zynqmp_dpsub_driver);
332d76271d2SHyun Kwon 
333d76271d2SHyun Kwon MODULE_AUTHOR("Xilinx, Inc.");
334d76271d2SHyun Kwon MODULE_DESCRIPTION("ZynqMP DP Subsystem Driver");
335d76271d2SHyun Kwon MODULE_LICENSE("GPL v2");
336