xref: /openbmc/linux/drivers/gpu/drm/meson/meson_drv.c (revision d55f7e5d)
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>
27bbbe775eSNeil Armstrong #include <linux/of_graph.h>
28bbbe775eSNeil Armstrong 
29bbbe775eSNeil Armstrong #include <drm/drmP.h>
30bbbe775eSNeil Armstrong #include <drm/drm_atomic.h>
31bbbe775eSNeil Armstrong #include <drm/drm_atomic_helper.h>
32bbbe775eSNeil Armstrong #include <drm/drm_flip_work.h>
33bbbe775eSNeil Armstrong #include <drm/drm_crtc_helper.h>
34bbbe775eSNeil Armstrong #include <drm/drm_plane_helper.h>
35bbbe775eSNeil Armstrong #include <drm/drm_gem_cma_helper.h>
36bbbe775eSNeil Armstrong #include <drm/drm_fb_cma_helper.h>
37bbbe775eSNeil Armstrong #include <drm/drm_rect.h>
38bbbe775eSNeil Armstrong #include <drm/drm_fb_helper.h>
39bbbe775eSNeil Armstrong 
40bbbe775eSNeil Armstrong #include "meson_drv.h"
41bbbe775eSNeil Armstrong #include "meson_plane.h"
42bbbe775eSNeil Armstrong #include "meson_crtc.h"
43bbbe775eSNeil Armstrong #include "meson_venc_cvbs.h"
44bbbe775eSNeil Armstrong 
45bbbe775eSNeil Armstrong #include "meson_vpp.h"
46bbbe775eSNeil Armstrong #include "meson_viu.h"
47bbbe775eSNeil Armstrong #include "meson_venc.h"
48bbbe775eSNeil Armstrong #include "meson_canvas.h"
49bbbe775eSNeil Armstrong #include "meson_registers.h"
50bbbe775eSNeil Armstrong 
51bbbe775eSNeil Armstrong #define DRIVER_NAME "meson"
52bbbe775eSNeil Armstrong #define DRIVER_DESC "Amlogic Meson DRM driver"
53bbbe775eSNeil Armstrong 
54bbbe775eSNeil Armstrong /*
55bbbe775eSNeil Armstrong  * Video Processing Unit
56bbbe775eSNeil Armstrong  *
57bbbe775eSNeil Armstrong  * VPU Handles the Global Video Processing, it includes management of the
58bbbe775eSNeil Armstrong  * clocks gates, blocks reset lines and power domains.
59bbbe775eSNeil Armstrong  *
60bbbe775eSNeil Armstrong  * What is missing :
61bbbe775eSNeil Armstrong  * - Full reset of entire video processing HW blocks
62bbbe775eSNeil Armstrong  * - Scaling and setup of the VPU clock
63bbbe775eSNeil Armstrong  * - Bus clock gates
64bbbe775eSNeil Armstrong  * - Powering up video processing HW blocks
65bbbe775eSNeil Armstrong  * - Powering Up HDMI controller and PHY
66bbbe775eSNeil Armstrong  */
67bbbe775eSNeil Armstrong 
68bbbe775eSNeil Armstrong static void meson_fb_output_poll_changed(struct drm_device *dev)
69bbbe775eSNeil Armstrong {
70bbbe775eSNeil Armstrong 	struct meson_drm *priv = dev->dev_private;
71bbbe775eSNeil Armstrong 
72bbbe775eSNeil Armstrong 	drm_fbdev_cma_hotplug_event(priv->fbdev);
73bbbe775eSNeil Armstrong }
74bbbe775eSNeil Armstrong 
75bbbe775eSNeil Armstrong static const struct drm_mode_config_funcs meson_mode_config_funcs = {
76bbbe775eSNeil Armstrong 	.output_poll_changed = meson_fb_output_poll_changed,
77bbbe775eSNeil Armstrong 	.atomic_check        = drm_atomic_helper_check,
78bbbe775eSNeil Armstrong 	.atomic_commit       = drm_atomic_helper_commit,
79bbbe775eSNeil Armstrong 	.fb_create           = drm_fb_cma_create,
80bbbe775eSNeil Armstrong };
81bbbe775eSNeil Armstrong 
82bbbe775eSNeil Armstrong static irqreturn_t meson_irq(int irq, void *arg)
83bbbe775eSNeil Armstrong {
84bbbe775eSNeil Armstrong 	struct drm_device *dev = arg;
85bbbe775eSNeil Armstrong 	struct meson_drm *priv = dev->dev_private;
86bbbe775eSNeil Armstrong 
87bbbe775eSNeil Armstrong 	(void)readl_relaxed(priv->io_base + _REG(VENC_INTFLAG));
88bbbe775eSNeil Armstrong 
89bbbe775eSNeil Armstrong 	meson_crtc_irq(priv);
90bbbe775eSNeil Armstrong 
91bbbe775eSNeil Armstrong 	return IRQ_HANDLED;
92bbbe775eSNeil Armstrong }
93bbbe775eSNeil Armstrong 
94d55f7e5dSDaniel Vetter DEFINE_DRM_GEM_CMA_FOPS(fops);
95bbbe775eSNeil Armstrong 
96bbbe775eSNeil Armstrong static struct drm_driver meson_driver = {
97bbbe775eSNeil Armstrong 	.driver_features	= DRIVER_HAVE_IRQ | DRIVER_GEM |
98bbbe775eSNeil Armstrong 				  DRIVER_MODESET | DRIVER_PRIME |
99bbbe775eSNeil Armstrong 				  DRIVER_ATOMIC,
100bbbe775eSNeil Armstrong 
101bbbe775eSNeil Armstrong 	/* IRQ */
102bbbe775eSNeil Armstrong 	.irq_handler		= meson_irq,
103bbbe775eSNeil Armstrong 
104bbbe775eSNeil Armstrong 	/* PRIME Ops */
105bbbe775eSNeil Armstrong 	.prime_handle_to_fd	= drm_gem_prime_handle_to_fd,
106bbbe775eSNeil Armstrong 	.prime_fd_to_handle	= drm_gem_prime_fd_to_handle,
107bbbe775eSNeil Armstrong 	.gem_prime_import	= drm_gem_prime_import,
108bbbe775eSNeil Armstrong 	.gem_prime_export	= drm_gem_prime_export,
109bbbe775eSNeil Armstrong 	.gem_prime_get_sg_table	= drm_gem_cma_prime_get_sg_table,
110bbbe775eSNeil Armstrong 	.gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
111bbbe775eSNeil Armstrong 	.gem_prime_vmap		= drm_gem_cma_prime_vmap,
112bbbe775eSNeil Armstrong 	.gem_prime_vunmap	= drm_gem_cma_prime_vunmap,
113bbbe775eSNeil Armstrong 	.gem_prime_mmap		= drm_gem_cma_prime_mmap,
114bbbe775eSNeil Armstrong 
115bbbe775eSNeil Armstrong 	/* GEM Ops */
116bbbe775eSNeil Armstrong 	.dumb_create		= drm_gem_cma_dumb_create,
117bbbe775eSNeil Armstrong 	.dumb_destroy		= drm_gem_dumb_destroy,
118bbbe775eSNeil Armstrong 	.dumb_map_offset	= drm_gem_cma_dumb_map_offset,
119bbbe775eSNeil Armstrong 	.gem_free_object_unlocked = drm_gem_cma_free_object,
120bbbe775eSNeil Armstrong 	.gem_vm_ops		= &drm_gem_cma_vm_ops,
121bbbe775eSNeil Armstrong 
122bbbe775eSNeil Armstrong 	/* Misc */
123bbbe775eSNeil Armstrong 	.fops			= &fops,
124bbbe775eSNeil Armstrong 	.name			= DRIVER_NAME,
125bbbe775eSNeil Armstrong 	.desc			= DRIVER_DESC,
126bbbe775eSNeil Armstrong 	.date			= "20161109",
127bbbe775eSNeil Armstrong 	.major			= 1,
128bbbe775eSNeil Armstrong 	.minor			= 0,
129bbbe775eSNeil Armstrong };
130bbbe775eSNeil Armstrong 
131bbbe775eSNeil Armstrong static bool meson_vpu_has_available_connectors(struct device *dev)
132bbbe775eSNeil Armstrong {
133bbbe775eSNeil Armstrong 	struct device_node *ep, *remote;
134bbbe775eSNeil Armstrong 
135bbbe775eSNeil Armstrong 	/* Parses each endpoint and check if remote exists */
136bbbe775eSNeil Armstrong 	for_each_endpoint_of_node(dev->of_node, ep) {
137bbbe775eSNeil Armstrong 		/* If the endpoint node exists, consider it enabled */
138bbbe775eSNeil Armstrong 		remote = of_graph_get_remote_port(ep);
139bbbe775eSNeil Armstrong 		if (remote)
140bbbe775eSNeil Armstrong 			return true;
141bbbe775eSNeil Armstrong 	}
142bbbe775eSNeil Armstrong 
143bbbe775eSNeil Armstrong 	return false;
144bbbe775eSNeil Armstrong }
145bbbe775eSNeil Armstrong 
146bbbe775eSNeil Armstrong static struct regmap_config meson_regmap_config = {
147bbbe775eSNeil Armstrong 	.reg_bits       = 32,
148bbbe775eSNeil Armstrong 	.val_bits       = 32,
149bbbe775eSNeil Armstrong 	.reg_stride     = 4,
150bbbe775eSNeil Armstrong 	.max_register   = 0x1000,
151bbbe775eSNeil Armstrong };
152bbbe775eSNeil Armstrong 
153bbbe775eSNeil Armstrong static int meson_drv_probe(struct platform_device *pdev)
154bbbe775eSNeil Armstrong {
155bbbe775eSNeil Armstrong 	struct device *dev = &pdev->dev;
156bbbe775eSNeil Armstrong 	struct meson_drm *priv;
157bbbe775eSNeil Armstrong 	struct drm_device *drm;
158bbbe775eSNeil Armstrong 	struct resource *res;
159bbbe775eSNeil Armstrong 	void __iomem *regs;
160bbbe775eSNeil Armstrong 	int ret;
161bbbe775eSNeil Armstrong 
162bbbe775eSNeil Armstrong 	/* Checks if an output connector is available */
163bbbe775eSNeil Armstrong 	if (!meson_vpu_has_available_connectors(dev)) {
164bbbe775eSNeil Armstrong 		dev_err(dev, "No output connector available\n");
165bbbe775eSNeil Armstrong 		return -ENODEV;
166bbbe775eSNeil Armstrong 	}
167bbbe775eSNeil Armstrong 
168bbbe775eSNeil Armstrong 	drm = drm_dev_alloc(&meson_driver, dev);
169bbbe775eSNeil Armstrong 	if (IS_ERR(drm))
170bbbe775eSNeil Armstrong 		return PTR_ERR(drm);
171bbbe775eSNeil Armstrong 
172bbbe775eSNeil Armstrong 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
173bbbe775eSNeil Armstrong 	if (!priv) {
174bbbe775eSNeil Armstrong 		ret = -ENOMEM;
175bbbe775eSNeil Armstrong 		goto free_drm;
176bbbe775eSNeil Armstrong 	}
177bbbe775eSNeil Armstrong 	drm->dev_private = priv;
178bbbe775eSNeil Armstrong 	priv->drm = drm;
179bbbe775eSNeil Armstrong 	priv->dev = dev;
180bbbe775eSNeil Armstrong 
181bbbe775eSNeil Armstrong 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vpu");
182bbbe775eSNeil Armstrong 	regs = devm_ioremap_resource(dev, res);
183bbbe775eSNeil Armstrong 	if (IS_ERR(regs))
184bbbe775eSNeil Armstrong 		return PTR_ERR(regs);
185bbbe775eSNeil Armstrong 
186bbbe775eSNeil Armstrong 	priv->io_base = regs;
187bbbe775eSNeil Armstrong 
188bbbe775eSNeil Armstrong 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hhi");
189bbbe775eSNeil Armstrong 	/* Simply ioremap since it may be a shared register zone */
190bbbe775eSNeil Armstrong 	regs = devm_ioremap(dev, res->start, resource_size(res));
191bbbe775eSNeil Armstrong 	if (!regs)
192bbbe775eSNeil Armstrong 		return -EADDRNOTAVAIL;
193bbbe775eSNeil Armstrong 
194bbbe775eSNeil Armstrong 	priv->hhi = devm_regmap_init_mmio(dev, regs,
195bbbe775eSNeil Armstrong 					  &meson_regmap_config);
196bbbe775eSNeil Armstrong 	if (IS_ERR(priv->hhi)) {
197bbbe775eSNeil Armstrong 		dev_err(&pdev->dev, "Couldn't create the HHI regmap\n");
198bbbe775eSNeil Armstrong 		return PTR_ERR(priv->hhi);
199bbbe775eSNeil Armstrong 	}
200bbbe775eSNeil Armstrong 
201bbbe775eSNeil Armstrong 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dmc");
202bbbe775eSNeil Armstrong 	/* Simply ioremap since it may be a shared register zone */
203bbbe775eSNeil Armstrong 	regs = devm_ioremap(dev, res->start, resource_size(res));
204bbbe775eSNeil Armstrong 	if (!regs)
205bbbe775eSNeil Armstrong 		return -EADDRNOTAVAIL;
206bbbe775eSNeil Armstrong 
207bbbe775eSNeil Armstrong 	priv->dmc = devm_regmap_init_mmio(dev, regs,
208bbbe775eSNeil Armstrong 					  &meson_regmap_config);
209bbbe775eSNeil Armstrong 	if (IS_ERR(priv->dmc)) {
210bbbe775eSNeil Armstrong 		dev_err(&pdev->dev, "Couldn't create the DMC regmap\n");
211bbbe775eSNeil Armstrong 		return PTR_ERR(priv->dmc);
212bbbe775eSNeil Armstrong 	}
213bbbe775eSNeil Armstrong 
214bbbe775eSNeil Armstrong 	priv->vsync_irq = platform_get_irq(pdev, 0);
215bbbe775eSNeil Armstrong 
216bbbe775eSNeil Armstrong 	drm_vblank_init(drm, 1);
217bbbe775eSNeil Armstrong 	drm_mode_config_init(drm);
218bbbe775eSNeil Armstrong 
219bbbe775eSNeil Armstrong 	/* Encoder Initialization */
220bbbe775eSNeil Armstrong 
221bbbe775eSNeil Armstrong 	ret = meson_venc_cvbs_create(priv);
222bbbe775eSNeil Armstrong 	if (ret)
223bbbe775eSNeil Armstrong 		goto free_drm;
224bbbe775eSNeil Armstrong 
225bbbe775eSNeil Armstrong 	/* Hardware Initialization */
226bbbe775eSNeil Armstrong 
227bbbe775eSNeil Armstrong 	meson_venc_init(priv);
228bbbe775eSNeil Armstrong 	meson_vpp_init(priv);
229bbbe775eSNeil Armstrong 	meson_viu_init(priv);
230bbbe775eSNeil Armstrong 
231bbbe775eSNeil Armstrong 	ret = meson_plane_create(priv);
232bbbe775eSNeil Armstrong 	if (ret)
233bbbe775eSNeil Armstrong 		goto free_drm;
234bbbe775eSNeil Armstrong 
235bbbe775eSNeil Armstrong 	ret = meson_crtc_create(priv);
236bbbe775eSNeil Armstrong 	if (ret)
237bbbe775eSNeil Armstrong 		goto free_drm;
238bbbe775eSNeil Armstrong 
239bbbe775eSNeil Armstrong 	ret = drm_irq_install(drm, priv->vsync_irq);
240bbbe775eSNeil Armstrong 	if (ret)
241bbbe775eSNeil Armstrong 		goto free_drm;
242bbbe775eSNeil Armstrong 
243bbbe775eSNeil Armstrong 	drm_mode_config_reset(drm);
244bbbe775eSNeil Armstrong 	drm->mode_config.max_width = 8192;
245bbbe775eSNeil Armstrong 	drm->mode_config.max_height = 8192;
246bbbe775eSNeil Armstrong 	drm->mode_config.funcs = &meson_mode_config_funcs;
247bbbe775eSNeil Armstrong 
248bbbe775eSNeil Armstrong 	priv->fbdev = drm_fbdev_cma_init(drm, 32,
249bbbe775eSNeil Armstrong 					 drm->mode_config.num_connector);
250bbbe775eSNeil Armstrong 	if (IS_ERR(priv->fbdev)) {
251bbbe775eSNeil Armstrong 		ret = PTR_ERR(priv->fbdev);
252bbbe775eSNeil Armstrong 		goto free_drm;
253bbbe775eSNeil Armstrong 	}
254bbbe775eSNeil Armstrong 
255bbbe775eSNeil Armstrong 	drm_kms_helper_poll_init(drm);
256bbbe775eSNeil Armstrong 
257bbbe775eSNeil Armstrong 	platform_set_drvdata(pdev, priv);
258bbbe775eSNeil Armstrong 
259bbbe775eSNeil Armstrong 	ret = drm_dev_register(drm, 0);
260bbbe775eSNeil Armstrong 	if (ret)
261bbbe775eSNeil Armstrong 		goto free_drm;
262bbbe775eSNeil Armstrong 
263bbbe775eSNeil Armstrong 	return 0;
264bbbe775eSNeil Armstrong 
265bbbe775eSNeil Armstrong free_drm:
266bbbe775eSNeil Armstrong 	drm_dev_unref(drm);
267bbbe775eSNeil Armstrong 
268bbbe775eSNeil Armstrong 	return ret;
269bbbe775eSNeil Armstrong }
270bbbe775eSNeil Armstrong 
271bbbe775eSNeil Armstrong static int meson_drv_remove(struct platform_device *pdev)
272bbbe775eSNeil Armstrong {
273bbbe775eSNeil Armstrong 	struct drm_device *drm = dev_get_drvdata(&pdev->dev);
274bbbe775eSNeil Armstrong 	struct meson_drm *priv = drm->dev_private;
275bbbe775eSNeil Armstrong 
276bbbe775eSNeil Armstrong 	drm_dev_unregister(drm);
277bbbe775eSNeil Armstrong 	drm_kms_helper_poll_fini(drm);
278bbbe775eSNeil Armstrong 	drm_fbdev_cma_fini(priv->fbdev);
279bbbe775eSNeil Armstrong 	drm_mode_config_cleanup(drm);
280bbbe775eSNeil Armstrong 	drm_vblank_cleanup(drm);
281bbbe775eSNeil Armstrong 	drm_dev_unref(drm);
282bbbe775eSNeil Armstrong 
283bbbe775eSNeil Armstrong 	return 0;
284bbbe775eSNeil Armstrong }
285bbbe775eSNeil Armstrong 
286bbbe775eSNeil Armstrong static const struct of_device_id dt_match[] = {
287bbbe775eSNeil Armstrong 	{ .compatible = "amlogic,meson-gxbb-vpu" },
288bbbe775eSNeil Armstrong 	{ .compatible = "amlogic,meson-gxl-vpu" },
289bbbe775eSNeil Armstrong 	{ .compatible = "amlogic,meson-gxm-vpu" },
290bbbe775eSNeil Armstrong 	{}
291bbbe775eSNeil Armstrong };
292bbbe775eSNeil Armstrong MODULE_DEVICE_TABLE(of, dt_match);
293bbbe775eSNeil Armstrong 
294bbbe775eSNeil Armstrong static struct platform_driver meson_drm_platform_driver = {
295bbbe775eSNeil Armstrong 	.probe      = meson_drv_probe,
296bbbe775eSNeil Armstrong 	.remove     = meson_drv_remove,
297bbbe775eSNeil Armstrong 	.driver     = {
2988aaacbc0SNeil Armstrong 		.name	= "meson-drm",
299bbbe775eSNeil Armstrong 		.of_match_table = dt_match,
300bbbe775eSNeil Armstrong 	},
301bbbe775eSNeil Armstrong };
302bbbe775eSNeil Armstrong 
303bbbe775eSNeil Armstrong module_platform_driver(meson_drm_platform_driver);
304bbbe775eSNeil Armstrong 
305bbbe775eSNeil Armstrong MODULE_AUTHOR("Jasper St. Pierre <jstpierre@mecheye.net>");
306bbbe775eSNeil Armstrong MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
307bbbe775eSNeil Armstrong MODULE_DESCRIPTION(DRIVER_DESC);
308bbbe775eSNeil Armstrong MODULE_LICENSE("GPL");
309