xref: /openbmc/linux/drivers/gpu/drm/meson/meson_drv.c (revision 4bf99144)
1bbbe775eSNeil Armstrong /*
2bbbe775eSNeil Armstrong  * Copyright (C) 2016 BayLibre, SAS
3bbbe775eSNeil Armstrong  * Author: Neil Armstrong <narmstrong@baylibre.com>
4bbbe775eSNeil Armstrong  * Copyright (C) 2014 Endless Mobile
5bbbe775eSNeil Armstrong  *
6bbbe775eSNeil Armstrong  * This program is free software; you can redistribute it and/or
7bbbe775eSNeil Armstrong  * modify it under the terms of the GNU General Public License as
8bbbe775eSNeil Armstrong  * published by the Free Software Foundation; either version 2 of the
9bbbe775eSNeil Armstrong  * License, or (at your option) any later version.
10bbbe775eSNeil Armstrong  *
11bbbe775eSNeil Armstrong  * This program is distributed in the hope that it will be useful, but
12bbbe775eSNeil Armstrong  * WITHOUT ANY WARRANTY; without even the implied warranty of
13bbbe775eSNeil Armstrong  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14bbbe775eSNeil Armstrong  * General Public License for more details.
15bbbe775eSNeil Armstrong  *
16bbbe775eSNeil Armstrong  * You should have received a copy of the GNU General Public License
17bbbe775eSNeil Armstrong  * along with this program; if not, see <http://www.gnu.org/licenses/>.
18bbbe775eSNeil Armstrong  *
19bbbe775eSNeil Armstrong  * Written by:
20bbbe775eSNeil Armstrong  *     Jasper St. Pierre <jstpierre@mecheye.net>
21bbbe775eSNeil Armstrong  */
22bbbe775eSNeil Armstrong 
23bbbe775eSNeil Armstrong #include <linux/kernel.h>
24bbbe775eSNeil Armstrong #include <linux/module.h>
25bbbe775eSNeil Armstrong #include <linux/mutex.h>
26bbbe775eSNeil Armstrong #include <linux/platform_device.h>
27a41e82e6SNeil Armstrong #include <linux/component.h>
28bbbe775eSNeil Armstrong #include <linux/of_graph.h>
29bbbe775eSNeil Armstrong 
30bbbe775eSNeil Armstrong #include <drm/drmP.h>
31bbbe775eSNeil Armstrong #include <drm/drm_atomic.h>
32bbbe775eSNeil Armstrong #include <drm/drm_atomic_helper.h>
33bbbe775eSNeil Armstrong #include <drm/drm_flip_work.h>
34bbbe775eSNeil Armstrong #include <drm/drm_crtc_helper.h>
35bbbe775eSNeil Armstrong #include <drm/drm_plane_helper.h>
36bbbe775eSNeil Armstrong #include <drm/drm_gem_cma_helper.h>
37bbbe775eSNeil Armstrong #include <drm/drm_fb_cma_helper.h>
38bbbe775eSNeil Armstrong #include <drm/drm_rect.h>
39bbbe775eSNeil Armstrong #include <drm/drm_fb_helper.h>
40bbbe775eSNeil Armstrong 
41bbbe775eSNeil Armstrong #include "meson_drv.h"
42bbbe775eSNeil Armstrong #include "meson_plane.h"
43bbbe775eSNeil Armstrong #include "meson_crtc.h"
44bbbe775eSNeil Armstrong #include "meson_venc_cvbs.h"
45bbbe775eSNeil Armstrong 
46bbbe775eSNeil Armstrong #include "meson_vpp.h"
47bbbe775eSNeil Armstrong #include "meson_viu.h"
48bbbe775eSNeil Armstrong #include "meson_venc.h"
49bbbe775eSNeil Armstrong #include "meson_canvas.h"
50bbbe775eSNeil Armstrong #include "meson_registers.h"
51bbbe775eSNeil Armstrong 
52bbbe775eSNeil Armstrong #define DRIVER_NAME "meson"
53bbbe775eSNeil Armstrong #define DRIVER_DESC "Amlogic Meson DRM driver"
54bbbe775eSNeil Armstrong 
552021d5b7SNeil Armstrong /**
562021d5b7SNeil Armstrong  * DOC: Video Processing Unit
57bbbe775eSNeil Armstrong  *
58bbbe775eSNeil Armstrong  * VPU Handles the Global Video Processing, it includes management of the
59bbbe775eSNeil Armstrong  * clocks gates, blocks reset lines and power domains.
60bbbe775eSNeil Armstrong  *
61bbbe775eSNeil Armstrong  * What is missing :
622021d5b7SNeil Armstrong  *
63bbbe775eSNeil Armstrong  * - Full reset of entire video processing HW blocks
64bbbe775eSNeil Armstrong  * - Scaling and setup of the VPU clock
65bbbe775eSNeil Armstrong  * - Bus clock gates
66bbbe775eSNeil Armstrong  * - Powering up video processing HW blocks
67bbbe775eSNeil Armstrong  * - Powering Up HDMI controller and PHY
68bbbe775eSNeil Armstrong  */
69bbbe775eSNeil Armstrong 
70bbbe775eSNeil Armstrong static void meson_fb_output_poll_changed(struct drm_device *dev)
71bbbe775eSNeil Armstrong {
72bbbe775eSNeil Armstrong 	struct meson_drm *priv = dev->dev_private;
73bbbe775eSNeil Armstrong 
74bbbe775eSNeil Armstrong 	drm_fbdev_cma_hotplug_event(priv->fbdev);
75bbbe775eSNeil Armstrong }
76bbbe775eSNeil Armstrong 
77bbbe775eSNeil Armstrong static const struct drm_mode_config_funcs meson_mode_config_funcs = {
78bbbe775eSNeil Armstrong 	.output_poll_changed = meson_fb_output_poll_changed,
79bbbe775eSNeil Armstrong 	.atomic_check        = drm_atomic_helper_check,
80bbbe775eSNeil Armstrong 	.atomic_commit       = drm_atomic_helper_commit,
81bbbe775eSNeil Armstrong 	.fb_create           = drm_fb_cma_create,
82bbbe775eSNeil Armstrong };
83bbbe775eSNeil Armstrong 
84bbbe775eSNeil Armstrong static irqreturn_t meson_irq(int irq, void *arg)
85bbbe775eSNeil Armstrong {
86bbbe775eSNeil Armstrong 	struct drm_device *dev = arg;
87bbbe775eSNeil Armstrong 	struct meson_drm *priv = dev->dev_private;
88bbbe775eSNeil Armstrong 
89bbbe775eSNeil Armstrong 	(void)readl_relaxed(priv->io_base + _REG(VENC_INTFLAG));
90bbbe775eSNeil Armstrong 
91bbbe775eSNeil Armstrong 	meson_crtc_irq(priv);
92bbbe775eSNeil Armstrong 
93bbbe775eSNeil Armstrong 	return IRQ_HANDLED;
94bbbe775eSNeil Armstrong }
95bbbe775eSNeil Armstrong 
96d55f7e5dSDaniel Vetter DEFINE_DRM_GEM_CMA_FOPS(fops);
97bbbe775eSNeil Armstrong 
98bbbe775eSNeil Armstrong static struct drm_driver meson_driver = {
99bbbe775eSNeil Armstrong 	.driver_features	= DRIVER_HAVE_IRQ | DRIVER_GEM |
100bbbe775eSNeil Armstrong 				  DRIVER_MODESET | DRIVER_PRIME |
101bbbe775eSNeil Armstrong 				  DRIVER_ATOMIC,
102bbbe775eSNeil Armstrong 
103bbbe775eSNeil Armstrong 	/* IRQ */
104bbbe775eSNeil Armstrong 	.irq_handler		= meson_irq,
105bbbe775eSNeil Armstrong 
106bbbe775eSNeil Armstrong 	/* PRIME Ops */
107bbbe775eSNeil Armstrong 	.prime_handle_to_fd	= drm_gem_prime_handle_to_fd,
108bbbe775eSNeil Armstrong 	.prime_fd_to_handle	= drm_gem_prime_fd_to_handle,
109bbbe775eSNeil Armstrong 	.gem_prime_import	= drm_gem_prime_import,
110bbbe775eSNeil Armstrong 	.gem_prime_export	= drm_gem_prime_export,
111bbbe775eSNeil Armstrong 	.gem_prime_get_sg_table	= drm_gem_cma_prime_get_sg_table,
112bbbe775eSNeil Armstrong 	.gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
113bbbe775eSNeil Armstrong 	.gem_prime_vmap		= drm_gem_cma_prime_vmap,
114bbbe775eSNeil Armstrong 	.gem_prime_vunmap	= drm_gem_cma_prime_vunmap,
115bbbe775eSNeil Armstrong 	.gem_prime_mmap		= drm_gem_cma_prime_mmap,
116bbbe775eSNeil Armstrong 
117bbbe775eSNeil Armstrong 	/* GEM Ops */
118bbbe775eSNeil Armstrong 	.dumb_create		= drm_gem_cma_dumb_create,
119bbbe775eSNeil Armstrong 	.dumb_destroy		= drm_gem_dumb_destroy,
120bbbe775eSNeil Armstrong 	.dumb_map_offset	= drm_gem_cma_dumb_map_offset,
121bbbe775eSNeil Armstrong 	.gem_free_object_unlocked = drm_gem_cma_free_object,
122bbbe775eSNeil Armstrong 	.gem_vm_ops		= &drm_gem_cma_vm_ops,
123bbbe775eSNeil Armstrong 
124bbbe775eSNeil Armstrong 	/* Misc */
125bbbe775eSNeil Armstrong 	.fops			= &fops,
126bbbe775eSNeil Armstrong 	.name			= DRIVER_NAME,
127bbbe775eSNeil Armstrong 	.desc			= DRIVER_DESC,
128bbbe775eSNeil Armstrong 	.date			= "20161109",
129bbbe775eSNeil Armstrong 	.major			= 1,
130bbbe775eSNeil Armstrong 	.minor			= 0,
131bbbe775eSNeil Armstrong };
132bbbe775eSNeil Armstrong 
133bbbe775eSNeil Armstrong static bool meson_vpu_has_available_connectors(struct device *dev)
134bbbe775eSNeil Armstrong {
135bbbe775eSNeil Armstrong 	struct device_node *ep, *remote;
136bbbe775eSNeil Armstrong 
137bbbe775eSNeil Armstrong 	/* Parses each endpoint and check if remote exists */
138bbbe775eSNeil Armstrong 	for_each_endpoint_of_node(dev->of_node, ep) {
139bbbe775eSNeil Armstrong 		/* If the endpoint node exists, consider it enabled */
140bbbe775eSNeil Armstrong 		remote = of_graph_get_remote_port(ep);
141bbbe775eSNeil Armstrong 		if (remote)
142bbbe775eSNeil Armstrong 			return true;
143bbbe775eSNeil Armstrong 	}
144bbbe775eSNeil Armstrong 
145bbbe775eSNeil Armstrong 	return false;
146bbbe775eSNeil Armstrong }
147bbbe775eSNeil Armstrong 
148bbbe775eSNeil Armstrong static struct regmap_config meson_regmap_config = {
149bbbe775eSNeil Armstrong 	.reg_bits       = 32,
150bbbe775eSNeil Armstrong 	.val_bits       = 32,
151bbbe775eSNeil Armstrong 	.reg_stride     = 4,
152bbbe775eSNeil Armstrong 	.max_register   = 0x1000,
153bbbe775eSNeil Armstrong };
154bbbe775eSNeil Armstrong 
1558604889fSNeil Armstrong static int meson_drv_bind_master(struct device *dev, bool has_components)
156bbbe775eSNeil Armstrong {
157a41e82e6SNeil Armstrong 	struct platform_device *pdev = to_platform_device(dev);
158bbbe775eSNeil Armstrong 	struct meson_drm *priv;
159bbbe775eSNeil Armstrong 	struct drm_device *drm;
160bbbe775eSNeil Armstrong 	struct resource *res;
161bbbe775eSNeil Armstrong 	void __iomem *regs;
162bbbe775eSNeil Armstrong 	int ret;
163bbbe775eSNeil Armstrong 
164bbbe775eSNeil Armstrong 	/* Checks if an output connector is available */
165bbbe775eSNeil Armstrong 	if (!meson_vpu_has_available_connectors(dev)) {
166bbbe775eSNeil Armstrong 		dev_err(dev, "No output connector available\n");
167bbbe775eSNeil Armstrong 		return -ENODEV;
168bbbe775eSNeil Armstrong 	}
169bbbe775eSNeil Armstrong 
170bbbe775eSNeil Armstrong 	drm = drm_dev_alloc(&meson_driver, dev);
171bbbe775eSNeil Armstrong 	if (IS_ERR(drm))
172bbbe775eSNeil Armstrong 		return PTR_ERR(drm);
173bbbe775eSNeil Armstrong 
174bbbe775eSNeil Armstrong 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
175bbbe775eSNeil Armstrong 	if (!priv) {
176bbbe775eSNeil Armstrong 		ret = -ENOMEM;
177bbbe775eSNeil Armstrong 		goto free_drm;
178bbbe775eSNeil Armstrong 	}
179bbbe775eSNeil Armstrong 	drm->dev_private = priv;
180bbbe775eSNeil Armstrong 	priv->drm = drm;
181bbbe775eSNeil Armstrong 	priv->dev = dev;
182bbbe775eSNeil Armstrong 
183bbbe775eSNeil Armstrong 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vpu");
184bbbe775eSNeil Armstrong 	regs = devm_ioremap_resource(dev, res);
185bbbe775eSNeil Armstrong 	if (IS_ERR(regs))
186bbbe775eSNeil Armstrong 		return PTR_ERR(regs);
187bbbe775eSNeil Armstrong 
188bbbe775eSNeil Armstrong 	priv->io_base = regs;
189bbbe775eSNeil Armstrong 
190bbbe775eSNeil Armstrong 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hhi");
191bbbe775eSNeil Armstrong 	/* Simply ioremap since it may be a shared register zone */
192bbbe775eSNeil Armstrong 	regs = devm_ioremap(dev, res->start, resource_size(res));
193bbbe775eSNeil Armstrong 	if (!regs)
194bbbe775eSNeil Armstrong 		return -EADDRNOTAVAIL;
195bbbe775eSNeil Armstrong 
196bbbe775eSNeil Armstrong 	priv->hhi = devm_regmap_init_mmio(dev, regs,
197bbbe775eSNeil Armstrong 					  &meson_regmap_config);
198bbbe775eSNeil Armstrong 	if (IS_ERR(priv->hhi)) {
199bbbe775eSNeil Armstrong 		dev_err(&pdev->dev, "Couldn't create the HHI regmap\n");
200bbbe775eSNeil Armstrong 		return PTR_ERR(priv->hhi);
201bbbe775eSNeil Armstrong 	}
202bbbe775eSNeil Armstrong 
203bbbe775eSNeil Armstrong 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dmc");
204bbbe775eSNeil Armstrong 	/* Simply ioremap since it may be a shared register zone */
205bbbe775eSNeil Armstrong 	regs = devm_ioremap(dev, res->start, resource_size(res));
206bbbe775eSNeil Armstrong 	if (!regs)
207bbbe775eSNeil Armstrong 		return -EADDRNOTAVAIL;
208bbbe775eSNeil Armstrong 
209bbbe775eSNeil Armstrong 	priv->dmc = devm_regmap_init_mmio(dev, regs,
210bbbe775eSNeil Armstrong 					  &meson_regmap_config);
211bbbe775eSNeil Armstrong 	if (IS_ERR(priv->dmc)) {
212bbbe775eSNeil Armstrong 		dev_err(&pdev->dev, "Couldn't create the DMC regmap\n");
213bbbe775eSNeil Armstrong 		return PTR_ERR(priv->dmc);
214bbbe775eSNeil Armstrong 	}
215bbbe775eSNeil Armstrong 
216bbbe775eSNeil Armstrong 	priv->vsync_irq = platform_get_irq(pdev, 0);
217bbbe775eSNeil Armstrong 
218bbbe775eSNeil Armstrong 	drm_vblank_init(drm, 1);
219bbbe775eSNeil Armstrong 	drm_mode_config_init(drm);
220a41e82e6SNeil Armstrong 	drm->mode_config.max_width = 3840;
221a41e82e6SNeil Armstrong 	drm->mode_config.max_height = 2160;
222a41e82e6SNeil Armstrong 	drm->mode_config.funcs = &meson_mode_config_funcs;
223a41e82e6SNeil Armstrong 
224a41e82e6SNeil Armstrong 	/* Hardware Initialization */
225a41e82e6SNeil Armstrong 
226a41e82e6SNeil Armstrong 	meson_venc_init(priv);
227a41e82e6SNeil Armstrong 	meson_vpp_init(priv);
228a41e82e6SNeil Armstrong 	meson_viu_init(priv);
229bbbe775eSNeil Armstrong 
230bbbe775eSNeil Armstrong 	/* Encoder Initialization */
231bbbe775eSNeil Armstrong 
232bbbe775eSNeil Armstrong 	ret = meson_venc_cvbs_create(priv);
233bbbe775eSNeil Armstrong 	if (ret)
234bbbe775eSNeil Armstrong 		goto free_drm;
235bbbe775eSNeil Armstrong 
2368604889fSNeil Armstrong 	if (has_components) {
237a41e82e6SNeil Armstrong 		ret = component_bind_all(drm->dev, drm);
238a41e82e6SNeil Armstrong 		if (ret) {
239a41e82e6SNeil Armstrong 			dev_err(drm->dev, "Couldn't bind all components\n");
240a41e82e6SNeil Armstrong 			goto free_drm;
241a41e82e6SNeil Armstrong 		}
2428604889fSNeil Armstrong 	}
243bbbe775eSNeil Armstrong 
244bbbe775eSNeil Armstrong 	ret = meson_plane_create(priv);
245bbbe775eSNeil Armstrong 	if (ret)
246bbbe775eSNeil Armstrong 		goto free_drm;
247bbbe775eSNeil Armstrong 
248bbbe775eSNeil Armstrong 	ret = meson_crtc_create(priv);
249bbbe775eSNeil Armstrong 	if (ret)
250bbbe775eSNeil Armstrong 		goto free_drm;
251bbbe775eSNeil Armstrong 
252bbbe775eSNeil Armstrong 	ret = drm_irq_install(drm, priv->vsync_irq);
253bbbe775eSNeil Armstrong 	if (ret)
254bbbe775eSNeil Armstrong 		goto free_drm;
255bbbe775eSNeil Armstrong 
256bbbe775eSNeil Armstrong 	drm_mode_config_reset(drm);
257bbbe775eSNeil Armstrong 
258bbbe775eSNeil Armstrong 	priv->fbdev = drm_fbdev_cma_init(drm, 32,
259bbbe775eSNeil Armstrong 					 drm->mode_config.num_connector);
260bbbe775eSNeil Armstrong 	if (IS_ERR(priv->fbdev)) {
261bbbe775eSNeil Armstrong 		ret = PTR_ERR(priv->fbdev);
262bbbe775eSNeil Armstrong 		goto free_drm;
263bbbe775eSNeil Armstrong 	}
264bbbe775eSNeil Armstrong 
265bbbe775eSNeil Armstrong 	drm_kms_helper_poll_init(drm);
266bbbe775eSNeil Armstrong 
267bbbe775eSNeil Armstrong 	platform_set_drvdata(pdev, priv);
268bbbe775eSNeil Armstrong 
269bbbe775eSNeil Armstrong 	ret = drm_dev_register(drm, 0);
270bbbe775eSNeil Armstrong 	if (ret)
271bbbe775eSNeil Armstrong 		goto free_drm;
272bbbe775eSNeil Armstrong 
273bbbe775eSNeil Armstrong 	return 0;
274bbbe775eSNeil Armstrong 
275bbbe775eSNeil Armstrong free_drm:
276bbbe775eSNeil Armstrong 	drm_dev_unref(drm);
277bbbe775eSNeil Armstrong 
278bbbe775eSNeil Armstrong 	return ret;
279bbbe775eSNeil Armstrong }
280bbbe775eSNeil Armstrong 
2818604889fSNeil Armstrong static int meson_drv_bind(struct device *dev)
2828604889fSNeil Armstrong {
2838604889fSNeil Armstrong 	return meson_drv_bind_master(dev, true);
2848604889fSNeil Armstrong }
2858604889fSNeil Armstrong 
286a41e82e6SNeil Armstrong static void meson_drv_unbind(struct device *dev)
287bbbe775eSNeil Armstrong {
288a41e82e6SNeil Armstrong 	struct drm_device *drm = dev_get_drvdata(dev);
289bbbe775eSNeil Armstrong 	struct meson_drm *priv = drm->dev_private;
290bbbe775eSNeil Armstrong 
291bbbe775eSNeil Armstrong 	drm_dev_unregister(drm);
292bbbe775eSNeil Armstrong 	drm_kms_helper_poll_fini(drm);
293bbbe775eSNeil Armstrong 	drm_fbdev_cma_fini(priv->fbdev);
294bbbe775eSNeil Armstrong 	drm_mode_config_cleanup(drm);
295bbbe775eSNeil Armstrong 	drm_dev_unref(drm);
296bbbe775eSNeil Armstrong 
297bbbe775eSNeil Armstrong }
298bbbe775eSNeil Armstrong 
299a41e82e6SNeil Armstrong static const struct component_master_ops meson_drv_master_ops = {
300a41e82e6SNeil Armstrong 	.bind	= meson_drv_bind,
301a41e82e6SNeil Armstrong 	.unbind	= meson_drv_unbind,
302a41e82e6SNeil Armstrong };
303a41e82e6SNeil Armstrong 
304a41e82e6SNeil Armstrong static int compare_of(struct device *dev, void *data)
305a41e82e6SNeil Armstrong {
3064bf99144SRob Herring 	DRM_DEBUG_DRIVER("Comparing of node %pOF with %pOF\n",
3074bf99144SRob Herring 			 dev->of_node, data);
308a41e82e6SNeil Armstrong 
309a41e82e6SNeil Armstrong 	return dev->of_node == data;
310a41e82e6SNeil Armstrong }
311a41e82e6SNeil Armstrong 
312a41e82e6SNeil Armstrong /* Possible connectors nodes to ignore */
313a41e82e6SNeil Armstrong static const struct of_device_id connectors_match[] = {
314a41e82e6SNeil Armstrong 	{ .compatible = "composite-video-connector" },
315a41e82e6SNeil Armstrong 	{ .compatible = "svideo-connector" },
316a41e82e6SNeil Armstrong 	{ .compatible = "hdmi-connector" },
317a41e82e6SNeil Armstrong 	{ .compatible = "dvi-connector" },
318a41e82e6SNeil Armstrong 	{}
319a41e82e6SNeil Armstrong };
320a41e82e6SNeil Armstrong 
321a41e82e6SNeil Armstrong static int meson_probe_remote(struct platform_device *pdev,
322a41e82e6SNeil Armstrong 			      struct component_match **match,
323a41e82e6SNeil Armstrong 			      struct device_node *parent,
324a41e82e6SNeil Armstrong 			      struct device_node *remote)
325a41e82e6SNeil Armstrong {
326a41e82e6SNeil Armstrong 	struct device_node *ep, *remote_node;
327a41e82e6SNeil Armstrong 	int count = 1;
328a41e82e6SNeil Armstrong 
329a41e82e6SNeil Armstrong 	/* If node is a connector, return and do not add to match table */
330a41e82e6SNeil Armstrong 	if (of_match_node(connectors_match, remote))
331a41e82e6SNeil Armstrong 		return 1;
332a41e82e6SNeil Armstrong 
333a41e82e6SNeil Armstrong 	component_match_add(&pdev->dev, match, compare_of, remote);
334a41e82e6SNeil Armstrong 
335a41e82e6SNeil Armstrong 	for_each_endpoint_of_node(remote, ep) {
336a41e82e6SNeil Armstrong 		remote_node = of_graph_get_remote_port_parent(ep);
337a41e82e6SNeil Armstrong 		if (!remote_node ||
338a41e82e6SNeil Armstrong 		    remote_node == parent || /* Ignore parent endpoint */
339a41e82e6SNeil Armstrong 		    !of_device_is_available(remote_node))
340a41e82e6SNeil Armstrong 			continue;
341a41e82e6SNeil Armstrong 
342a41e82e6SNeil Armstrong 		count += meson_probe_remote(pdev, match, remote, remote_node);
343a41e82e6SNeil Armstrong 
344a41e82e6SNeil Armstrong 		of_node_put(remote_node);
345a41e82e6SNeil Armstrong 	}
346a41e82e6SNeil Armstrong 
347a41e82e6SNeil Armstrong 	return count;
348a41e82e6SNeil Armstrong }
349a41e82e6SNeil Armstrong 
350a41e82e6SNeil Armstrong static int meson_drv_probe(struct platform_device *pdev)
351a41e82e6SNeil Armstrong {
352a41e82e6SNeil Armstrong 	struct component_match *match = NULL;
353a41e82e6SNeil Armstrong 	struct device_node *np = pdev->dev.of_node;
354a41e82e6SNeil Armstrong 	struct device_node *ep, *remote;
355a41e82e6SNeil Armstrong 	int count = 0;
356a41e82e6SNeil Armstrong 
357a41e82e6SNeil Armstrong 	for_each_endpoint_of_node(np, ep) {
358a41e82e6SNeil Armstrong 		remote = of_graph_get_remote_port_parent(ep);
359a41e82e6SNeil Armstrong 		if (!remote || !of_device_is_available(remote))
360a41e82e6SNeil Armstrong 			continue;
361a41e82e6SNeil Armstrong 
362a41e82e6SNeil Armstrong 		count += meson_probe_remote(pdev, &match, np, remote);
363a41e82e6SNeil Armstrong 	}
364a41e82e6SNeil Armstrong 
3658604889fSNeil Armstrong 	if (count && !match)
3668604889fSNeil Armstrong 		return meson_drv_bind_master(&pdev->dev, false);
3678604889fSNeil Armstrong 
368a41e82e6SNeil Armstrong 	/* If some endpoints were found, initialize the nodes */
369a41e82e6SNeil Armstrong 	if (count) {
370a41e82e6SNeil Armstrong 		dev_info(&pdev->dev, "Queued %d outputs on vpu\n", count);
371a41e82e6SNeil Armstrong 
372a41e82e6SNeil Armstrong 		return component_master_add_with_match(&pdev->dev,
373a41e82e6SNeil Armstrong 						       &meson_drv_master_ops,
374a41e82e6SNeil Armstrong 						       match);
375a41e82e6SNeil Armstrong 	}
376a41e82e6SNeil Armstrong 
377a41e82e6SNeil Armstrong 	/* If no output endpoints were available, simply bail out */
378a41e82e6SNeil Armstrong 	return 0;
379a41e82e6SNeil Armstrong };
380a41e82e6SNeil Armstrong 
381bbbe775eSNeil Armstrong static const struct of_device_id dt_match[] = {
382bbbe775eSNeil Armstrong 	{ .compatible = "amlogic,meson-gxbb-vpu" },
383bbbe775eSNeil Armstrong 	{ .compatible = "amlogic,meson-gxl-vpu" },
384bbbe775eSNeil Armstrong 	{ .compatible = "amlogic,meson-gxm-vpu" },
385bbbe775eSNeil Armstrong 	{}
386bbbe775eSNeil Armstrong };
387bbbe775eSNeil Armstrong MODULE_DEVICE_TABLE(of, dt_match);
388bbbe775eSNeil Armstrong 
389bbbe775eSNeil Armstrong static struct platform_driver meson_drm_platform_driver = {
390bbbe775eSNeil Armstrong 	.probe      = meson_drv_probe,
391bbbe775eSNeil Armstrong 	.driver     = {
3928aaacbc0SNeil Armstrong 		.name	= "meson-drm",
393bbbe775eSNeil Armstrong 		.of_match_table = dt_match,
394bbbe775eSNeil Armstrong 	},
395bbbe775eSNeil Armstrong };
396bbbe775eSNeil Armstrong 
397bbbe775eSNeil Armstrong module_platform_driver(meson_drm_platform_driver);
398bbbe775eSNeil Armstrong 
399bbbe775eSNeil Armstrong MODULE_AUTHOR("Jasper St. Pierre <jstpierre@mecheye.net>");
400bbbe775eSNeil Armstrong MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
401bbbe775eSNeil Armstrong MODULE_DESCRIPTION(DRIVER_DESC);
402bbbe775eSNeil Armstrong MODULE_LICENSE("GPL");
403