1 // SPDX-License-Identifier: GPL-2.0+
3 * Copyright (C) 2019-2022 Bootlin
4 * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
7 #include <linux/bitfield.h>
9 #include <linux/mfd/syscon.h>
10 #include <linux/module.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>
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>
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"
34 DEFINE_DRM_GEM_DMA_FOPS(logicvc_drm_fops
);
36 static 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
)
40 struct logicvc_drm
*logicvc
= logicvc_drm(drm_dev
);
42 /* Stride is always fixed to its configuration value. */
43 args
->pitch
= logicvc
->config
.row_stride
* DIV_ROUND_UP(args
->bpp
, 8);
45 return drm_gem_dma_dumb_create_internal(file_priv
, drm_dev
, args
);
48 static struct drm_driver logicvc_drm_driver
= {
49 .driver_features
= DRIVER_GEM
| DRIVER_MODESET
|
52 .fops
= &logicvc_drm_fops
,
53 .name
= "logicvc-drm",
54 .desc
= "Xylon LogiCVC DRM driver",
59 DRM_GEM_DMA_DRIVER_OPS_VMAP_WITH_DUMB_CREATE(logicvc_drm_gem_dma_dumb_create
),
60 DRM_FBDEV_DMA_DRIVER_OPS
,
63 static struct regmap_config logicvc_drm_regmap_config
= {
67 .name
= "logicvc-drm",
70 static irqreturn_t
logicvc_drm_irq_handler(int irq
, void *data
)
72 struct logicvc_drm
*logicvc
= data
;
73 irqreturn_t ret
= IRQ_NONE
;
76 /* Get pending interrupt sources. */
77 regmap_read(logicvc
->regmap
, LOGICVC_INT_STAT_REG
, &stat
);
79 /* Clear all pending interrupt sources. */
80 regmap_write(logicvc
->regmap
, LOGICVC_INT_STAT_REG
, stat
);
82 if (stat
& LOGICVC_INT_STAT_V_SYNC
) {
83 logicvc_crtc_vblank_handler(logicvc
);
90 static int logicvc_drm_config_parse(struct logicvc_drm
*logicvc
)
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
;
99 logicvc_of_property_parse_bool(of_node
, LOGICVC_OF_PROPERTY_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
);
108 ret
= logicvc_of_property_parse_u32(of_node
,
109 LOGICVC_OF_PROPERTY_DISPLAY_INTERFACE
,
110 &config
->display_interface
);
114 ret
= logicvc_of_property_parse_u32(of_node
,
115 LOGICVC_OF_PROPERTY_DISPLAY_COLORSPACE
,
116 &config
->display_colorspace
);
120 ret
= logicvc_of_property_parse_u32(of_node
,
121 LOGICVC_OF_PROPERTY_DISPLAY_DEPTH
,
122 &config
->display_depth
);
126 ret
= logicvc_of_property_parse_u32(of_node
,
127 LOGICVC_OF_PROPERTY_ROW_STRIDE
,
128 &config
->row_stride
);
132 layers_node
= of_get_child_by_name(of_node
, "layers");
134 drm_err(drm_dev
, "Missing non-optional layers node\n");
138 config
->layers_count
= of_get_child_count(layers_node
);
139 if (!config
->layers_count
) {
141 "Missing a non-optional layers children node\n");
148 static int logicvc_clocks_prepare(struct logicvc_drm
*logicvc
)
150 struct drm_device
*drm_dev
= &logicvc
->drm_dev
;
151 struct device
*dev
= drm_dev
->dev
;
159 .clk
= &logicvc
->vclk
,
164 .clk
= &logicvc
->vclk2
,
169 .clk
= &logicvc
->lvdsclk
,
174 .clk
= &logicvc
->lvdsclkn
,
182 for (i
= 0; i
< ARRAY_SIZE(clocks_map
); i
++) {
185 clk
= devm_clk_get(dev
, clocks_map
[i
].name
);
187 if (PTR_ERR(clk
) == -ENOENT
&& clocks_map
[i
].optional
)
190 drm_err(drm_dev
, "Missing non-optional clock %s\n",
197 ret
= clk_prepare_enable(clk
);
200 "Failed to prepare and enable clock %s\n",
205 *clocks_map
[i
].clk
= clk
;
211 for (i
= 0; i
< ARRAY_SIZE(clocks_map
); i
++) {
212 if (!*clocks_map
[i
].clk
)
215 clk_disable_unprepare(*clocks_map
[i
].clk
);
216 *clocks_map
[i
].clk
= NULL
;
222 static int logicvc_clocks_unprepare(struct logicvc_drm
*logicvc
)
224 struct clk
**clocks
[] = {
232 for (i
= 0; i
< ARRAY_SIZE(clocks
); i
++) {
236 clk_disable_unprepare(*clocks
[i
]);
243 static const struct logicvc_drm_caps logicvc_drm_caps
[] = {
246 .layer_address
= false,
250 .layer_address
= true,
254 .layer_address
= true,
258 static const struct logicvc_drm_caps
*
259 logicvc_drm_caps_match(struct logicvc_drm
*logicvc
)
261 struct drm_device
*drm_dev
= &logicvc
->drm_dev
;
262 const struct logicvc_drm_caps
*caps
= NULL
;
263 unsigned int major
, minor
;
268 regmap_read(logicvc
->regmap
, LOGICVC_IP_VERSION_REG
, &version
);
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';
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
)
279 if (logicvc_drm_caps
[i
].minor
&&
280 logicvc_drm_caps
[i
].minor
!= minor
)
283 if (logicvc_drm_caps
[i
].level
&&
284 logicvc_drm_caps
[i
].level
!= level
)
287 caps
= &logicvc_drm_caps
[i
];
290 drm_info(drm_dev
, "LogiCVC version %d.%02d.%c\n", major
, minor
, level
);
295 static int logicvc_drm_probe(struct platform_device
*pdev
)
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
;
310 ret
= of_reserved_mem_device_init(dev
);
311 if (ret
&& ret
!= -ENODEV
) {
312 dev_err(dev
, "Failed to init memory region\n");
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
);
322 /* Get regmap from parent if available. */
324 regmap
= syscon_node_to_regmap(of_node
->parent
);
326 /* Register our own regmap otherwise. */
327 if (IS_ERR_OR_NULL(regmap
)) {
328 ret
= of_address_to_resource(of_node
, 0, &res
);
330 dev_err(dev
, "Failed to get resource from address\n");
331 goto error_reserved_mem
;
334 base
= devm_ioremap_resource(dev
, &res
);
336 dev_err(dev
, "Failed to map I/O base\n");
338 goto error_reserved_mem
;
341 logicvc_drm_regmap_config
.max_register
= resource_size(&res
) -
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
;
353 irq
= platform_get_irq(pdev
, 0);
356 goto error_reserved_mem
;
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
;
366 platform_set_drvdata(pdev
, logicvc
);
367 drm_dev
= &logicvc
->drm_dev
;
369 logicvc
->regmap
= regmap
;
370 INIT_LIST_HEAD(&logicvc
->layers_list
);
372 caps
= logicvc_drm_caps_match(logicvc
);
375 goto error_reserved_mem
;
378 logicvc
->caps
= caps
;
381 logicvc
->reserved_mem_base
= reserved_mem
->base
;
383 ret
= logicvc_clocks_prepare(logicvc
);
385 drm_err(drm_dev
, "Failed to prepare clocks\n");
386 goto error_reserved_mem
;
389 ret
= devm_request_irq(dev
, irq
, logicvc_drm_irq_handler
, 0,
390 dev_name(dev
), logicvc
);
392 drm_err(drm_dev
, "Failed to request IRQ\n");
396 ret
= logicvc_drm_config_parse(logicvc
);
397 if (ret
&& ret
!= -ENODEV
) {
398 drm_err(drm_dev
, "Failed to parse config\n");
402 ret
= drmm_mode_config_init(drm_dev
);
404 drm_err(drm_dev
, "Failed to init mode config\n");
408 ret
= logicvc_layers_init(logicvc
);
410 drm_err(drm_dev
, "Failed to initialize layers\n");
414 ret
= logicvc_crtc_init(logicvc
);
416 drm_err(drm_dev
, "Failed to initialize CRTC\n");
420 logicvc_layers_attach_crtc(logicvc
);
422 ret
= logicvc_interface_init(logicvc
);
424 if (ret
!= -EPROBE_DEFER
)
425 drm_err(drm_dev
, "Failed to initialize interface\n");
430 logicvc_interface_attach_crtc(logicvc
);
432 ret
= logicvc_mode_init(logicvc
);
434 drm_err(drm_dev
, "Failed to initialize KMS\n");
438 ret
= drm_dev_register(drm_dev
, 0);
440 drm_err(drm_dev
, "Failed to register DRM device\n");
444 drm_client_setup(drm_dev
, NULL
);
449 logicvc_mode_fini(logicvc
);
452 logicvc_clocks_unprepare(logicvc
);
455 of_reserved_mem_device_release(dev
);
461 static void logicvc_drm_remove(struct platform_device
*pdev
)
463 struct logicvc_drm
*logicvc
= platform_get_drvdata(pdev
);
464 struct device
*dev
= &pdev
->dev
;
465 struct drm_device
*drm_dev
= &logicvc
->drm_dev
;
467 drm_dev_unregister(drm_dev
);
468 drm_atomic_helper_shutdown(drm_dev
);
470 logicvc_mode_fini(logicvc
);
472 logicvc_clocks_unprepare(logicvc
);
474 of_reserved_mem_device_release(dev
);
477 static void logicvc_drm_shutdown(struct platform_device
*pdev
)
479 struct logicvc_drm
*logicvc
= platform_get_drvdata(pdev
);
480 struct drm_device
*drm_dev
= &logicvc
->drm_dev
;
482 drm_atomic_helper_shutdown(drm_dev
);
485 static 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" },
490 MODULE_DEVICE_TABLE(of
, logicvc_drm_of_table
);
492 static struct platform_driver logicvc_drm_platform_driver
= {
493 .probe
= logicvc_drm_probe
,
494 .remove
= logicvc_drm_remove
,
495 .shutdown
= logicvc_drm_shutdown
,
497 .name
= "logicvc-drm",
498 .of_match_table
= logicvc_drm_of_table
,
502 module_platform_driver(logicvc_drm_platform_driver
);
504 MODULE_AUTHOR("Paul Kocialkowski <paul.kocialkowski@bootlin.com>");
505 MODULE_DESCRIPTION("Xylon LogiCVC DRM driver");
506 MODULE_LICENSE("GPL");