Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1// SPDX-License-Identifier: GPL-2.0+
  2/*
  3 * Copyright (C) 2019-2022 Bootlin
  4 * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
  5 */
  6
  7#include <linux/bitfield.h>
  8#include <linux/clk.h>
  9#include <linux/mfd/syscon.h>
 10#include <linux/module.h>
 11#include <linux/of.h>
 12#include <linux/of_address.h>
 13#include <linux/of_reserved_mem.h>
 14#include <linux/platform_device.h>
 15#include <linux/regmap.h>
 16#include <linux/types.h>
 17
 18#include <drm/drm_atomic_helper.h>
 19#include <drm/drm_client_setup.h>
 20#include <drm/drm_drv.h>
 21#include <drm/drm_fbdev_dma.h>
 22#include <drm/drm_fourcc.h>
 23#include <drm/drm_gem_dma_helper.h>
 24#include <drm/drm_print.h>
 25
 26#include "logicvc_crtc.h"
 27#include "logicvc_drm.h"
 28#include "logicvc_interface.h"
 29#include "logicvc_mode.h"
 30#include "logicvc_layer.h"
 31#include "logicvc_of.h"
 32#include "logicvc_regs.h"
 33
 34DEFINE_DRM_GEM_DMA_FOPS(logicvc_drm_fops);
 35
 36static int logicvc_drm_gem_dma_dumb_create(struct drm_file *file_priv,
 37					   struct drm_device *drm_dev,
 38					   struct drm_mode_create_dumb *args)
 39{
 40	struct logicvc_drm *logicvc = logicvc_drm(drm_dev);
 41
 42	/* Stride is always fixed to its configuration value. */
 43	args->pitch = logicvc->config.row_stride * DIV_ROUND_UP(args->bpp, 8);
 44
 45	return drm_gem_dma_dumb_create_internal(file_priv, drm_dev, args);
 46}
 47
 48static struct drm_driver logicvc_drm_driver = {
 49	.driver_features		= DRIVER_GEM | DRIVER_MODESET |
 50					  DRIVER_ATOMIC,
 51
 52	.fops				= &logicvc_drm_fops,
 53	.name				= "logicvc-drm",
 54	.desc				= "Xylon LogiCVC DRM driver",
 55	.date				= "20200403",
 56	.major				= 1,
 57	.minor				= 0,
 58
 59	DRM_GEM_DMA_DRIVER_OPS_VMAP_WITH_DUMB_CREATE(logicvc_drm_gem_dma_dumb_create),
 60	DRM_FBDEV_DMA_DRIVER_OPS,
 61};
 62
 63static struct regmap_config logicvc_drm_regmap_config = {
 64	.reg_bits	= 32,
 65	.val_bits	= 32,
 66	.reg_stride	= 4,
 67	.name		= "logicvc-drm",
 68};
 69
 70static irqreturn_t logicvc_drm_irq_handler(int irq, void *data)
 71{
 72	struct logicvc_drm *logicvc = data;
 73	irqreturn_t ret = IRQ_NONE;
 74	u32 stat = 0;
 75
 76	/* Get pending interrupt sources. */
 77	regmap_read(logicvc->regmap, LOGICVC_INT_STAT_REG, &stat);
 78
 79	/* Clear all pending interrupt sources. */
 80	regmap_write(logicvc->regmap, LOGICVC_INT_STAT_REG, stat);
 81
 82	if (stat & LOGICVC_INT_STAT_V_SYNC) {
 83		logicvc_crtc_vblank_handler(logicvc);
 84		ret = IRQ_HANDLED;
 85	}
 86
 87	return ret;
 88}
 89
 90static int logicvc_drm_config_parse(struct logicvc_drm *logicvc)
 91{
 92	struct drm_device *drm_dev = &logicvc->drm_dev;
 93	struct device *dev = drm_dev->dev;
 94	struct device_node *of_node = dev->of_node;
 95	struct logicvc_drm_config *config = &logicvc->config;
 96	struct device_node *layers_node;
 97	int ret;
 98
 99	logicvc_of_property_parse_bool(of_node, LOGICVC_OF_PROPERTY_DITHERING,
100				       &config->dithering);
101	logicvc_of_property_parse_bool(of_node,
102				       LOGICVC_OF_PROPERTY_BACKGROUND_LAYER,
103				       &config->background_layer);
104	logicvc_of_property_parse_bool(of_node,
105				       LOGICVC_OF_PROPERTY_LAYERS_CONFIGURABLE,
106				       &config->layers_configurable);
107
108	ret = logicvc_of_property_parse_u32(of_node,
109					    LOGICVC_OF_PROPERTY_DISPLAY_INTERFACE,
110					    &config->display_interface);
111	if (ret)
112		return ret;
113
114	ret = logicvc_of_property_parse_u32(of_node,
115					    LOGICVC_OF_PROPERTY_DISPLAY_COLORSPACE,
116					    &config->display_colorspace);
117	if (ret)
118		return ret;
119
120	ret = logicvc_of_property_parse_u32(of_node,
121					    LOGICVC_OF_PROPERTY_DISPLAY_DEPTH,
122					    &config->display_depth);
123	if (ret)
124		return ret;
125
126	ret = logicvc_of_property_parse_u32(of_node,
127					    LOGICVC_OF_PROPERTY_ROW_STRIDE,
128					    &config->row_stride);
129	if (ret)
130		return ret;
131
132	layers_node = of_get_child_by_name(of_node, "layers");
133	if (!layers_node) {
134		drm_err(drm_dev, "Missing non-optional layers node\n");
135		return -EINVAL;
136	}
137
138	config->layers_count = of_get_child_count(layers_node);
139	if (!config->layers_count) {
140		drm_err(drm_dev,
141			"Missing a non-optional layers children node\n");
142		return -EINVAL;
143	}
144
145	return 0;
146}
147
148static int logicvc_clocks_prepare(struct logicvc_drm *logicvc)
149{
150	struct drm_device *drm_dev = &logicvc->drm_dev;
151	struct device *dev = drm_dev->dev;
152
153	struct {
154		struct clk **clk;
155		char *name;
156		bool optional;
157	} clocks_map[] = {
158		{
159			.clk = &logicvc->vclk,
160			.name = "vclk",
161			.optional = false,
162		},
163		{
164			.clk = &logicvc->vclk2,
165			.name = "vclk2",
166			.optional = true,
167		},
168		{
169			.clk = &logicvc->lvdsclk,
170			.name = "lvdsclk",
171			.optional = true,
172		},
173		{
174			.clk = &logicvc->lvdsclkn,
175			.name = "lvdsclkn",
176			.optional = true,
177		},
178	};
179	unsigned int i;
180	int ret;
181
182	for (i = 0; i < ARRAY_SIZE(clocks_map); i++) {
183		struct clk *clk;
184
185		clk = devm_clk_get(dev, clocks_map[i].name);
186		if (IS_ERR(clk)) {
187			if (PTR_ERR(clk) == -ENOENT && clocks_map[i].optional)
188				continue;
189
190			drm_err(drm_dev, "Missing non-optional clock %s\n",
191				clocks_map[i].name);
192
193			ret = PTR_ERR(clk);
194			goto error;
195		}
196
197		ret = clk_prepare_enable(clk);
198		if (ret) {
199			drm_err(drm_dev,
200				"Failed to prepare and enable clock %s\n",
201				clocks_map[i].name);
202			goto error;
203		}
204
205		*clocks_map[i].clk = clk;
206	}
207
208	return 0;
209
210error:
211	for (i = 0; i < ARRAY_SIZE(clocks_map); i++) {
212		if (!*clocks_map[i].clk)
213			continue;
214
215		clk_disable_unprepare(*clocks_map[i].clk);
216		*clocks_map[i].clk = NULL;
217	}
218
219	return ret;
220}
221
222static int logicvc_clocks_unprepare(struct logicvc_drm *logicvc)
223{
224	struct clk **clocks[] = {
225		&logicvc->vclk,
226		&logicvc->vclk2,
227		&logicvc->lvdsclk,
228		&logicvc->lvdsclkn,
229	};
230	unsigned int i;
231
232	for (i = 0; i < ARRAY_SIZE(clocks); i++) {
233		if (!*clocks[i])
234			continue;
235
236		clk_disable_unprepare(*clocks[i]);
237		*clocks[i] = NULL;
238	}
239
240	return 0;
241}
242
243static const struct logicvc_drm_caps logicvc_drm_caps[] = {
244	{
245		.major		= 3,
246		.layer_address	= false,
247	},
248	{
249		.major		= 4,
250		.layer_address	= true,
251	},
252	{
253		.major		= 5,
254		.layer_address	= true,
255	},
256};
257
258static const struct logicvc_drm_caps *
259logicvc_drm_caps_match(struct logicvc_drm *logicvc)
260{
261	struct drm_device *drm_dev = &logicvc->drm_dev;
262	const struct logicvc_drm_caps *caps = NULL;
263	unsigned int major, minor;
264	char level;
265	unsigned int i;
266	u32 version;
267
268	regmap_read(logicvc->regmap, LOGICVC_IP_VERSION_REG, &version);
269
270	major = FIELD_GET(LOGICVC_IP_VERSION_MAJOR_MASK, version);
271	minor = FIELD_GET(LOGICVC_IP_VERSION_MINOR_MASK, version);
272	level = FIELD_GET(LOGICVC_IP_VERSION_LEVEL_MASK, version) + 'a';
273
274	for (i = 0; i < ARRAY_SIZE(logicvc_drm_caps); i++) {
275		if (logicvc_drm_caps[i].major &&
276		    logicvc_drm_caps[i].major != major)
277			continue;
278
279		if (logicvc_drm_caps[i].minor &&
280		    logicvc_drm_caps[i].minor != minor)
281			continue;
282
283		if (logicvc_drm_caps[i].level &&
284		    logicvc_drm_caps[i].level != level)
285			continue;
286
287		caps = &logicvc_drm_caps[i];
288	}
289
290	drm_info(drm_dev, "LogiCVC version %d.%02d.%c\n", major, minor, level);
291
292	return caps;
293}
294
295static int logicvc_drm_probe(struct platform_device *pdev)
296{
297	struct device_node *of_node = pdev->dev.of_node;
298	struct device_node *reserved_mem_node;
299	struct reserved_mem *reserved_mem = NULL;
300	const struct logicvc_drm_caps *caps;
301	struct logicvc_drm *logicvc;
302	struct device *dev = &pdev->dev;
303	struct drm_device *drm_dev;
304	struct regmap *regmap = NULL;
305	struct resource res;
306	void __iomem *base;
307	int irq;
308	int ret;
309
310	ret = of_reserved_mem_device_init(dev);
311	if (ret && ret != -ENODEV) {
312		dev_err(dev, "Failed to init memory region\n");
313		goto error_early;
314	}
315
316	reserved_mem_node = of_parse_phandle(of_node, "memory-region", 0);
317	if (reserved_mem_node) {
318		reserved_mem = of_reserved_mem_lookup(reserved_mem_node);
319		of_node_put(reserved_mem_node);
320	}
321
322	/* Get regmap from parent if available. */
323	if (of_node->parent)
324		regmap = syscon_node_to_regmap(of_node->parent);
325
326	/* Register our own regmap otherwise. */
327	if (IS_ERR_OR_NULL(regmap)) {
328		ret = of_address_to_resource(of_node, 0, &res);
329		if (ret) {
330			dev_err(dev, "Failed to get resource from address\n");
331			goto error_reserved_mem;
332		}
333
334		base = devm_ioremap_resource(dev, &res);
335		if (IS_ERR(base)) {
336			dev_err(dev, "Failed to map I/O base\n");
337			ret = PTR_ERR(base);
338			goto error_reserved_mem;
339		}
340
341		logicvc_drm_regmap_config.max_register = resource_size(&res) -
342							 4;
343
344		regmap = devm_regmap_init_mmio(dev, base,
345					       &logicvc_drm_regmap_config);
346		if (IS_ERR(regmap)) {
347			dev_err(dev, "Failed to create regmap for I/O\n");
348			ret = PTR_ERR(regmap);
349			goto error_reserved_mem;
350		}
351	}
352
353	irq = platform_get_irq(pdev, 0);
354	if (irq < 0) {
355		ret = -ENODEV;
356		goto error_reserved_mem;
357	}
358
359	logicvc = devm_drm_dev_alloc(dev, &logicvc_drm_driver,
360				     struct logicvc_drm, drm_dev);
361	if (IS_ERR(logicvc)) {
362		ret = PTR_ERR(logicvc);
363		goto error_reserved_mem;
364	}
365
366	platform_set_drvdata(pdev, logicvc);
367	drm_dev = &logicvc->drm_dev;
368
369	logicvc->regmap = regmap;
370	INIT_LIST_HEAD(&logicvc->layers_list);
371
372	caps = logicvc_drm_caps_match(logicvc);
373	if (!caps) {
374		ret = -EINVAL;
375		goto error_reserved_mem;
376	}
377
378	logicvc->caps = caps;
379
380	if (reserved_mem)
381		logicvc->reserved_mem_base = reserved_mem->base;
382
383	ret = logicvc_clocks_prepare(logicvc);
384	if (ret) {
385		drm_err(drm_dev, "Failed to prepare clocks\n");
386		goto error_reserved_mem;
387	}
388
389	ret = devm_request_irq(dev, irq, logicvc_drm_irq_handler, 0,
390			       dev_name(dev), logicvc);
391	if (ret) {
392		drm_err(drm_dev, "Failed to request IRQ\n");
393		goto error_clocks;
394	}
395
396	ret = logicvc_drm_config_parse(logicvc);
397	if (ret && ret != -ENODEV) {
398		drm_err(drm_dev, "Failed to parse config\n");
399		goto error_clocks;
400	}
401
402	ret = drmm_mode_config_init(drm_dev);
403	if (ret) {
404		drm_err(drm_dev, "Failed to init mode config\n");
405		goto error_clocks;
406	}
407
408	ret = logicvc_layers_init(logicvc);
409	if (ret) {
410		drm_err(drm_dev, "Failed to initialize layers\n");
411		goto error_clocks;
412	}
413
414	ret = logicvc_crtc_init(logicvc);
415	if (ret) {
416		drm_err(drm_dev, "Failed to initialize CRTC\n");
417		goto error_clocks;
418	}
419
420	logicvc_layers_attach_crtc(logicvc);
421
422	ret = logicvc_interface_init(logicvc);
423	if (ret) {
424		if (ret != -EPROBE_DEFER)
425			drm_err(drm_dev, "Failed to initialize interface\n");
426
427		goto error_clocks;
428	}
429
430	logicvc_interface_attach_crtc(logicvc);
431
432	ret = logicvc_mode_init(logicvc);
433	if (ret) {
434		drm_err(drm_dev, "Failed to initialize KMS\n");
435		goto error_clocks;
436	}
437
438	ret = drm_dev_register(drm_dev, 0);
439	if (ret) {
440		drm_err(drm_dev, "Failed to register DRM device\n");
441		goto error_mode;
442	}
443
444	drm_client_setup(drm_dev, NULL);
445
446	return 0;
447
448error_mode:
449	logicvc_mode_fini(logicvc);
450
451error_clocks:
452	logicvc_clocks_unprepare(logicvc);
453
454error_reserved_mem:
455	of_reserved_mem_device_release(dev);
456
457error_early:
458	return ret;
459}
460
461static void logicvc_drm_remove(struct platform_device *pdev)
462{
463	struct logicvc_drm *logicvc = platform_get_drvdata(pdev);
464	struct device *dev = &pdev->dev;
465	struct drm_device *drm_dev = &logicvc->drm_dev;
466
467	drm_dev_unregister(drm_dev);
468	drm_atomic_helper_shutdown(drm_dev);
469
470	logicvc_mode_fini(logicvc);
471
472	logicvc_clocks_unprepare(logicvc);
473
474	of_reserved_mem_device_release(dev);
475}
476
477static void logicvc_drm_shutdown(struct platform_device *pdev)
478{
479	struct logicvc_drm *logicvc = platform_get_drvdata(pdev);
480	struct drm_device *drm_dev = &logicvc->drm_dev;
481
482	drm_atomic_helper_shutdown(drm_dev);
483}
484
485static const struct of_device_id logicvc_drm_of_table[] = {
486	{ .compatible = "xylon,logicvc-3.02.a-display" },
487	{ .compatible = "xylon,logicvc-4.01.a-display" },
488	{},
489};
490MODULE_DEVICE_TABLE(of, logicvc_drm_of_table);
491
492static struct platform_driver logicvc_drm_platform_driver = {
493	.probe		= logicvc_drm_probe,
494	.remove		= logicvc_drm_remove,
495	.shutdown	= logicvc_drm_shutdown,
496	.driver		= {
497		.name		= "logicvc-drm",
498		.of_match_table	= logicvc_drm_of_table,
499	},
500};
501
502module_platform_driver(logicvc_drm_platform_driver);
503
504MODULE_AUTHOR("Paul Kocialkowski <paul.kocialkowski@bootlin.com>");
505MODULE_DESCRIPTION("Xylon LogiCVC DRM driver");
506MODULE_LICENSE("GPL");