Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.13.7.
  1/*
  2 * rcar_du_drv.c  --  R-Car Display Unit DRM driver
  3 *
  4 * Copyright (C) 2013-2015 Renesas Electronics Corporation
  5 *
  6 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
  7 *
  8 * This program is free software; you can redistribute it and/or modify
  9 * it under the terms of the GNU General Public License as published by
 10 * the Free Software Foundation; either version 2 of the License, or
 11 * (at your option) any later version.
 12 */
 13
 14#include <linux/clk.h>
 15#include <linux/io.h>
 16#include <linux/mm.h>
 17#include <linux/module.h>
 18#include <linux/of_device.h>
 19#include <linux/platform_device.h>
 20#include <linux/pm.h>
 21#include <linux/slab.h>
 22#include <linux/wait.h>
 23
 24#include <drm/drmP.h>
 25#include <drm/drm_crtc_helper.h>
 26#include <drm/drm_fb_cma_helper.h>
 27#include <drm/drm_gem_cma_helper.h>
 28
 29#include "rcar_du_crtc.h"
 30#include "rcar_du_drv.h"
 31#include "rcar_du_kms.h"
 32#include "rcar_du_regs.h"
 33
 34/* -----------------------------------------------------------------------------
 35 * Device Information
 36 */
 37
 38static const struct rcar_du_device_info rcar_du_r8a7779_info = {
 39	.gen = 2,
 40	.features = 0,
 41	.num_crtcs = 2,
 42	.routes = {
 43		/* R8A7779 has two RGB outputs and one (currently unsupported)
 44		 * TCON output.
 45		 */
 46		[RCAR_DU_OUTPUT_DPAD0] = {
 47			.possible_crtcs = BIT(0),
 48			.encoder_type = DRM_MODE_ENCODER_NONE,
 49			.port = 0,
 50		},
 51		[RCAR_DU_OUTPUT_DPAD1] = {
 52			.possible_crtcs = BIT(1) | BIT(0),
 53			.encoder_type = DRM_MODE_ENCODER_NONE,
 54			.port = 1,
 55		},
 56	},
 57	.num_lvds = 0,
 58};
 59
 60static const struct rcar_du_device_info rcar_du_r8a7790_info = {
 61	.gen = 2,
 62	.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
 63		  | RCAR_DU_FEATURE_EXT_CTRL_REGS,
 64	.quirks = RCAR_DU_QUIRK_ALIGN_128B | RCAR_DU_QUIRK_LVDS_LANES,
 65	.num_crtcs = 3,
 66	.routes = {
 67		/* R8A7790 has one RGB output, two LVDS outputs and one
 68		 * (currently unsupported) TCON output.
 69		 */
 70		[RCAR_DU_OUTPUT_DPAD0] = {
 71			.possible_crtcs = BIT(2) | BIT(1) | BIT(0),
 72			.encoder_type = DRM_MODE_ENCODER_NONE,
 73			.port = 0,
 74		},
 75		[RCAR_DU_OUTPUT_LVDS0] = {
 76			.possible_crtcs = BIT(0),
 77			.encoder_type = DRM_MODE_ENCODER_LVDS,
 78			.port = 1,
 79		},
 80		[RCAR_DU_OUTPUT_LVDS1] = {
 81			.possible_crtcs = BIT(2) | BIT(1),
 82			.encoder_type = DRM_MODE_ENCODER_LVDS,
 83			.port = 2,
 84		},
 85	},
 86	.num_lvds = 2,
 87};
 88
 89/* M2-W (r8a7791) and M2-N (r8a7793) are identical */
 90static const struct rcar_du_device_info rcar_du_r8a7791_info = {
 91	.gen = 2,
 92	.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
 93		  | RCAR_DU_FEATURE_EXT_CTRL_REGS,
 94	.num_crtcs = 2,
 95	.routes = {
 96		/* R8A779[13] has one RGB output, one LVDS output and one
 97		 * (currently unsupported) TCON output.
 98		 */
 99		[RCAR_DU_OUTPUT_DPAD0] = {
100			.possible_crtcs = BIT(1) | BIT(0),
101			.encoder_type = DRM_MODE_ENCODER_NONE,
102			.port = 0,
103		},
104		[RCAR_DU_OUTPUT_LVDS0] = {
105			.possible_crtcs = BIT(0),
106			.encoder_type = DRM_MODE_ENCODER_LVDS,
107			.port = 1,
108		},
109	},
110	.num_lvds = 1,
111};
112
113static const struct rcar_du_device_info rcar_du_r8a7792_info = {
114	.gen = 2,
115	.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
116		  | RCAR_DU_FEATURE_EXT_CTRL_REGS,
117	.num_crtcs = 2,
118	.routes = {
119		/* R8A7792 has two RGB outputs. */
120		[RCAR_DU_OUTPUT_DPAD0] = {
121			.possible_crtcs = BIT(0),
122			.encoder_type = DRM_MODE_ENCODER_NONE,
123			.port = 0,
124		},
125		[RCAR_DU_OUTPUT_DPAD1] = {
126			.possible_crtcs = BIT(1),
127			.encoder_type = DRM_MODE_ENCODER_NONE,
128			.port = 1,
129		},
130	},
131	.num_lvds = 0,
132};
133
134static const struct rcar_du_device_info rcar_du_r8a7794_info = {
135	.gen = 2,
136	.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
137		  | RCAR_DU_FEATURE_EXT_CTRL_REGS,
138	.num_crtcs = 2,
139	.routes = {
140		/* R8A7794 has two RGB outputs and one (currently unsupported)
141		 * TCON output.
142		 */
143		[RCAR_DU_OUTPUT_DPAD0] = {
144			.possible_crtcs = BIT(0),
145			.encoder_type = DRM_MODE_ENCODER_NONE,
146			.port = 0,
147		},
148		[RCAR_DU_OUTPUT_DPAD1] = {
149			.possible_crtcs = BIT(1),
150			.encoder_type = DRM_MODE_ENCODER_NONE,
151			.port = 1,
152		},
153	},
154	.num_lvds = 0,
155};
156
157static const struct rcar_du_device_info rcar_du_r8a7795_info = {
158	.gen = 3,
159	.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
160		  | RCAR_DU_FEATURE_EXT_CTRL_REGS
161		  | RCAR_DU_FEATURE_VSP1_SOURCE,
162	.num_crtcs = 4,
163	.routes = {
164		/* R8A7795 has one RGB output, one LVDS output and two
165		 * (currently unsupported) HDMI outputs.
166		 */
167		[RCAR_DU_OUTPUT_DPAD0] = {
168			.possible_crtcs = BIT(3),
169			.encoder_type = DRM_MODE_ENCODER_NONE,
170			.port = 0,
171		},
172		[RCAR_DU_OUTPUT_LVDS0] = {
173			.possible_crtcs = BIT(0),
174			.encoder_type = DRM_MODE_ENCODER_LVDS,
175			.port = 3,
176		},
177	},
178	.num_lvds = 1,
179};
180
181static const struct rcar_du_device_info rcar_du_r8a7796_info = {
182	.gen = 3,
183	.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
184		  | RCAR_DU_FEATURE_EXT_CTRL_REGS
185		  | RCAR_DU_FEATURE_VSP1_SOURCE,
186	.num_crtcs = 3,
187	.routes = {
188		/* R8A7796 has one RGB output, one LVDS output and one
189		 * (currently unsupported) HDMI output.
190		 */
191		[RCAR_DU_OUTPUT_DPAD0] = {
192			.possible_crtcs = BIT(2),
193			.encoder_type = DRM_MODE_ENCODER_NONE,
194			.port = 0,
195		},
196		[RCAR_DU_OUTPUT_LVDS0] = {
197			.possible_crtcs = BIT(0),
198			.encoder_type = DRM_MODE_ENCODER_LVDS,
199			.port = 2,
200		},
201	},
202	.num_lvds = 1,
203};
204
205static const struct of_device_id rcar_du_of_table[] = {
206	{ .compatible = "renesas,du-r8a7779", .data = &rcar_du_r8a7779_info },
207	{ .compatible = "renesas,du-r8a7790", .data = &rcar_du_r8a7790_info },
208	{ .compatible = "renesas,du-r8a7791", .data = &rcar_du_r8a7791_info },
209	{ .compatible = "renesas,du-r8a7792", .data = &rcar_du_r8a7792_info },
210	{ .compatible = "renesas,du-r8a7793", .data = &rcar_du_r8a7791_info },
211	{ .compatible = "renesas,du-r8a7794", .data = &rcar_du_r8a7794_info },
212	{ .compatible = "renesas,du-r8a7795", .data = &rcar_du_r8a7795_info },
213	{ .compatible = "renesas,du-r8a7796", .data = &rcar_du_r8a7796_info },
214	{ }
215};
216
217MODULE_DEVICE_TABLE(of, rcar_du_of_table);
218
219/* -----------------------------------------------------------------------------
220 * DRM operations
221 */
222
223static void rcar_du_lastclose(struct drm_device *dev)
224{
225	struct rcar_du_device *rcdu = dev->dev_private;
226
227	drm_fbdev_cma_restore_mode(rcdu->fbdev);
228}
229
230static int rcar_du_enable_vblank(struct drm_device *dev, unsigned int pipe)
231{
232	struct rcar_du_device *rcdu = dev->dev_private;
233
234	rcar_du_crtc_enable_vblank(&rcdu->crtcs[pipe], true);
235
236	return 0;
237}
238
239static void rcar_du_disable_vblank(struct drm_device *dev, unsigned int pipe)
240{
241	struct rcar_du_device *rcdu = dev->dev_private;
242
243	rcar_du_crtc_enable_vblank(&rcdu->crtcs[pipe], false);
244}
245
246static const struct file_operations rcar_du_fops = {
247	.owner		= THIS_MODULE,
248	.open		= drm_open,
249	.release	= drm_release,
250	.unlocked_ioctl	= drm_ioctl,
251	.compat_ioctl	= drm_compat_ioctl,
252	.poll		= drm_poll,
253	.read		= drm_read,
254	.llseek		= no_llseek,
255	.mmap		= drm_gem_cma_mmap,
256};
257
258static struct drm_driver rcar_du_driver = {
259	.driver_features	= DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME
260				| DRIVER_ATOMIC,
261	.lastclose		= rcar_du_lastclose,
262	.get_vblank_counter	= drm_vblank_no_hw_counter,
263	.enable_vblank		= rcar_du_enable_vblank,
264	.disable_vblank		= rcar_du_disable_vblank,
265	.gem_free_object_unlocked = drm_gem_cma_free_object,
266	.gem_vm_ops		= &drm_gem_cma_vm_ops,
267	.prime_handle_to_fd	= drm_gem_prime_handle_to_fd,
268	.prime_fd_to_handle	= drm_gem_prime_fd_to_handle,
269	.gem_prime_import	= drm_gem_prime_import,
270	.gem_prime_export	= drm_gem_prime_export,
271	.gem_prime_get_sg_table	= drm_gem_cma_prime_get_sg_table,
272	.gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
273	.gem_prime_vmap		= drm_gem_cma_prime_vmap,
274	.gem_prime_vunmap	= drm_gem_cma_prime_vunmap,
275	.gem_prime_mmap		= drm_gem_cma_prime_mmap,
276	.dumb_create		= rcar_du_dumb_create,
277	.dumb_map_offset	= drm_gem_cma_dumb_map_offset,
278	.dumb_destroy		= drm_gem_dumb_destroy,
279	.fops			= &rcar_du_fops,
280	.name			= "rcar-du",
281	.desc			= "Renesas R-Car Display Unit",
282	.date			= "20130110",
283	.major			= 1,
284	.minor			= 0,
285};
286
287/* -----------------------------------------------------------------------------
288 * Power management
289 */
290
291#ifdef CONFIG_PM_SLEEP
292static int rcar_du_pm_suspend(struct device *dev)
293{
294	struct rcar_du_device *rcdu = dev_get_drvdata(dev);
295
296	drm_kms_helper_poll_disable(rcdu->ddev);
297	/* TODO Suspend the CRTC */
298
299	return 0;
300}
301
302static int rcar_du_pm_resume(struct device *dev)
303{
304	struct rcar_du_device *rcdu = dev_get_drvdata(dev);
305
306	/* TODO Resume the CRTC */
307
308	drm_kms_helper_poll_enable(rcdu->ddev);
309	return 0;
310}
311#endif
312
313static const struct dev_pm_ops rcar_du_pm_ops = {
314	SET_SYSTEM_SLEEP_PM_OPS(rcar_du_pm_suspend, rcar_du_pm_resume)
315};
316
317/* -----------------------------------------------------------------------------
318 * Platform driver
319 */
320
321static int rcar_du_remove(struct platform_device *pdev)
322{
323	struct rcar_du_device *rcdu = platform_get_drvdata(pdev);
324	struct drm_device *ddev = rcdu->ddev;
325
326	drm_dev_unregister(ddev);
327
328	if (rcdu->fbdev)
329		drm_fbdev_cma_fini(rcdu->fbdev);
330
331	drm_kms_helper_poll_fini(ddev);
332	drm_mode_config_cleanup(ddev);
333
334	drm_dev_unref(ddev);
335
336	return 0;
337}
338
339static int rcar_du_probe(struct platform_device *pdev)
340{
341	struct rcar_du_device *rcdu;
342	struct drm_device *ddev;
343	struct resource *mem;
344	int ret;
345
346	/* Allocate and initialize the R-Car device structure. */
347	rcdu = devm_kzalloc(&pdev->dev, sizeof(*rcdu), GFP_KERNEL);
348	if (rcdu == NULL)
349		return -ENOMEM;
350
351	init_waitqueue_head(&rcdu->commit.wait);
352
353	rcdu->dev = &pdev->dev;
354	rcdu->info = of_match_device(rcar_du_of_table, rcdu->dev)->data;
355
356	platform_set_drvdata(pdev, rcdu);
357
358	/* I/O resources */
359	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
360	rcdu->mmio = devm_ioremap_resource(&pdev->dev, mem);
361	if (IS_ERR(rcdu->mmio))
362		return PTR_ERR(rcdu->mmio);
363
364	/* DRM/KMS objects */
365	ddev = drm_dev_alloc(&rcar_du_driver, &pdev->dev);
366	if (IS_ERR(ddev))
367		return PTR_ERR(ddev);
368
369	rcdu->ddev = ddev;
370	ddev->dev_private = rcdu;
371
372	ret = rcar_du_modeset_init(rcdu);
373	if (ret < 0) {
374		if (ret != -EPROBE_DEFER)
375			dev_err(&pdev->dev,
376				"failed to initialize DRM/KMS (%d)\n", ret);
377		goto error;
378	}
379
380	ddev->irq_enabled = 1;
381
382	/* Register the DRM device with the core and the connectors with
383	 * sysfs.
384	 */
385	ret = drm_dev_register(ddev, 0);
386	if (ret)
387		goto error;
388
389	DRM_INFO("Device %s probed\n", dev_name(&pdev->dev));
390
391	return 0;
392
393error:
394	rcar_du_remove(pdev);
395
396	return ret;
397}
398
399static struct platform_driver rcar_du_platform_driver = {
400	.probe		= rcar_du_probe,
401	.remove		= rcar_du_remove,
402	.driver		= {
403		.name	= "rcar-du",
404		.pm	= &rcar_du_pm_ops,
405		.of_match_table = rcar_du_of_table,
406	},
407};
408
409module_platform_driver(rcar_du_platform_driver);
410
411MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
412MODULE_DESCRIPTION("Renesas R-Car Display Unit DRM Driver");
413MODULE_LICENSE("GPL");