xref: /linux/drivers/gpu/drm/verisilicon/vs_drm.c (revision 0fc8f6200d2313278fbf4539bbab74677c685531)
1*dbf21777SIcenowy Zheng // SPDX-License-Identifier: GPL-2.0-only
2*dbf21777SIcenowy Zheng /*
3*dbf21777SIcenowy Zheng  * Copyright (C) 2025 Icenowy Zheng <uwu@icenowy.me>
4*dbf21777SIcenowy Zheng  */
5*dbf21777SIcenowy Zheng 
6*dbf21777SIcenowy Zheng #include <linux/aperture.h>
7*dbf21777SIcenowy Zheng #include <linux/dma-mapping.h>
8*dbf21777SIcenowy Zheng #include <linux/platform_device.h>
9*dbf21777SIcenowy Zheng #include <linux/module.h>
10*dbf21777SIcenowy Zheng #include <linux/regmap.h>
11*dbf21777SIcenowy Zheng #include <linux/console.h>
12*dbf21777SIcenowy Zheng 
13*dbf21777SIcenowy Zheng #include <drm/clients/drm_client_setup.h>
14*dbf21777SIcenowy Zheng #include <drm/drm_atomic_helper.h>
15*dbf21777SIcenowy Zheng #include <drm/drm_drv.h>
16*dbf21777SIcenowy Zheng #include <drm/drm_dumb_buffers.h>
17*dbf21777SIcenowy Zheng #include <drm/drm_fbdev_dma.h>
18*dbf21777SIcenowy Zheng #include <drm/drm_gem_dma_helper.h>
19*dbf21777SIcenowy Zheng #include <drm/drm_gem_framebuffer_helper.h>
20*dbf21777SIcenowy Zheng #include <drm/drm_of.h>
21*dbf21777SIcenowy Zheng #include <drm/drm_print.h>
22*dbf21777SIcenowy Zheng #include <drm/drm_probe_helper.h>
23*dbf21777SIcenowy Zheng #include <drm/drm_vblank.h>
24*dbf21777SIcenowy Zheng 
25*dbf21777SIcenowy Zheng #include "vs_bridge.h"
26*dbf21777SIcenowy Zheng #include "vs_crtc.h"
27*dbf21777SIcenowy Zheng #include "vs_dc.h"
28*dbf21777SIcenowy Zheng #include "vs_dc_top_regs.h"
29*dbf21777SIcenowy Zheng #include "vs_drm.h"
30*dbf21777SIcenowy Zheng 
31*dbf21777SIcenowy Zheng #define DRIVER_NAME	"verisilicon"
32*dbf21777SIcenowy Zheng #define DRIVER_DESC	"Verisilicon DC-series display controller driver"
33*dbf21777SIcenowy Zheng #define DRIVER_MAJOR	1
34*dbf21777SIcenowy Zheng #define DRIVER_MINOR	0
35*dbf21777SIcenowy Zheng 
36*dbf21777SIcenowy Zheng static int vs_gem_dumb_create(struct drm_file *file_priv,
37*dbf21777SIcenowy Zheng 			      struct drm_device *drm,
38*dbf21777SIcenowy Zheng 			      struct drm_mode_create_dumb *args)
39*dbf21777SIcenowy Zheng {
40*dbf21777SIcenowy Zheng 	int ret;
41*dbf21777SIcenowy Zheng 
42*dbf21777SIcenowy Zheng 	/* The hardware wants 128B-aligned pitches for linear buffers. */
43*dbf21777SIcenowy Zheng 	ret = drm_mode_size_dumb(drm, args, 128, 0);
44*dbf21777SIcenowy Zheng 	if (ret)
45*dbf21777SIcenowy Zheng 		return ret;
46*dbf21777SIcenowy Zheng 
47*dbf21777SIcenowy Zheng 	return drm_gem_dma_dumb_create_internal(file_priv, drm, args);
48*dbf21777SIcenowy Zheng }
49*dbf21777SIcenowy Zheng 
50*dbf21777SIcenowy Zheng DEFINE_DRM_GEM_FOPS(vs_drm_driver_fops);
51*dbf21777SIcenowy Zheng 
52*dbf21777SIcenowy Zheng static const struct drm_driver vs_drm_driver = {
53*dbf21777SIcenowy Zheng 	.driver_features	= DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC,
54*dbf21777SIcenowy Zheng 	.fops			= &vs_drm_driver_fops,
55*dbf21777SIcenowy Zheng 	.name	= DRIVER_NAME,
56*dbf21777SIcenowy Zheng 	.desc	= DRIVER_DESC,
57*dbf21777SIcenowy Zheng 	.major	= DRIVER_MAJOR,
58*dbf21777SIcenowy Zheng 	.minor	= DRIVER_MINOR,
59*dbf21777SIcenowy Zheng 
60*dbf21777SIcenowy Zheng 	/* GEM Operations */
61*dbf21777SIcenowy Zheng 	DRM_GEM_DMA_DRIVER_OPS_WITH_DUMB_CREATE(vs_gem_dumb_create),
62*dbf21777SIcenowy Zheng 	DRM_FBDEV_DMA_DRIVER_OPS,
63*dbf21777SIcenowy Zheng };
64*dbf21777SIcenowy Zheng 
65*dbf21777SIcenowy Zheng static const struct drm_mode_config_funcs vs_mode_config_funcs = {
66*dbf21777SIcenowy Zheng 	.fb_create		= drm_gem_fb_create,
67*dbf21777SIcenowy Zheng 	.atomic_check		= drm_atomic_helper_check,
68*dbf21777SIcenowy Zheng 	.atomic_commit		= drm_atomic_helper_commit,
69*dbf21777SIcenowy Zheng };
70*dbf21777SIcenowy Zheng 
71*dbf21777SIcenowy Zheng static struct drm_mode_config_helper_funcs vs_mode_config_helper_funcs = {
72*dbf21777SIcenowy Zheng 	.atomic_commit_tail = drm_atomic_helper_commit_tail,
73*dbf21777SIcenowy Zheng };
74*dbf21777SIcenowy Zheng 
75*dbf21777SIcenowy Zheng static void vs_mode_config_init(struct drm_device *drm)
76*dbf21777SIcenowy Zheng {
77*dbf21777SIcenowy Zheng 	drm->mode_config.min_width = 0;
78*dbf21777SIcenowy Zheng 	drm->mode_config.min_height = 0;
79*dbf21777SIcenowy Zheng 	drm->mode_config.max_width = 8192;
80*dbf21777SIcenowy Zheng 	drm->mode_config.max_height = 8192;
81*dbf21777SIcenowy Zheng 	drm->mode_config.funcs = &vs_mode_config_funcs;
82*dbf21777SIcenowy Zheng 	drm->mode_config.helper_private = &vs_mode_config_helper_funcs;
83*dbf21777SIcenowy Zheng }
84*dbf21777SIcenowy Zheng 
85*dbf21777SIcenowy Zheng int vs_drm_initialize(struct vs_dc *dc, struct platform_device *pdev)
86*dbf21777SIcenowy Zheng {
87*dbf21777SIcenowy Zheng 	struct device *dev = &pdev->dev;
88*dbf21777SIcenowy Zheng 	struct vs_drm_dev *vdrm;
89*dbf21777SIcenowy Zheng 	struct drm_device *drm;
90*dbf21777SIcenowy Zheng 	struct vs_crtc *crtc;
91*dbf21777SIcenowy Zheng 	struct vs_bridge *bridge;
92*dbf21777SIcenowy Zheng 	unsigned int i;
93*dbf21777SIcenowy Zheng 	int ret;
94*dbf21777SIcenowy Zheng 
95*dbf21777SIcenowy Zheng 	vdrm = devm_drm_dev_alloc(dev, &vs_drm_driver, struct vs_drm_dev, base);
96*dbf21777SIcenowy Zheng 	if (IS_ERR(vdrm))
97*dbf21777SIcenowy Zheng 		return PTR_ERR(vdrm);
98*dbf21777SIcenowy Zheng 
99*dbf21777SIcenowy Zheng 	drm = &vdrm->base;
100*dbf21777SIcenowy Zheng 	vdrm->dc = dc;
101*dbf21777SIcenowy Zheng 	dc->drm_dev = vdrm;
102*dbf21777SIcenowy Zheng 
103*dbf21777SIcenowy Zheng 	ret = drmm_mode_config_init(drm);
104*dbf21777SIcenowy Zheng 	if (ret)
105*dbf21777SIcenowy Zheng 		return ret;
106*dbf21777SIcenowy Zheng 
107*dbf21777SIcenowy Zheng 	/* Remove early framebuffers (ie. simple-framebuffer) */
108*dbf21777SIcenowy Zheng 	ret = aperture_remove_all_conflicting_devices(DRIVER_NAME);
109*dbf21777SIcenowy Zheng 	if (ret)
110*dbf21777SIcenowy Zheng 		return ret;
111*dbf21777SIcenowy Zheng 
112*dbf21777SIcenowy Zheng 	for (i = 0; i < dc->identity.display_count; i++) {
113*dbf21777SIcenowy Zheng 		crtc = vs_crtc_init(drm, dc, i);
114*dbf21777SIcenowy Zheng 		if (IS_ERR(crtc))
115*dbf21777SIcenowy Zheng 			return PTR_ERR(crtc);
116*dbf21777SIcenowy Zheng 
117*dbf21777SIcenowy Zheng 		bridge = vs_bridge_init(drm, crtc);
118*dbf21777SIcenowy Zheng 		if (IS_ERR(bridge))
119*dbf21777SIcenowy Zheng 			return PTR_ERR(bridge);
120*dbf21777SIcenowy Zheng 
121*dbf21777SIcenowy Zheng 		vdrm->crtcs[i] = crtc;
122*dbf21777SIcenowy Zheng 	}
123*dbf21777SIcenowy Zheng 
124*dbf21777SIcenowy Zheng 	ret = drm_vblank_init(drm, dc->identity.display_count);
125*dbf21777SIcenowy Zheng 	if (ret)
126*dbf21777SIcenowy Zheng 		return ret;
127*dbf21777SIcenowy Zheng 
128*dbf21777SIcenowy Zheng 	vs_mode_config_init(drm);
129*dbf21777SIcenowy Zheng 
130*dbf21777SIcenowy Zheng 	/* Enable connectors polling */
131*dbf21777SIcenowy Zheng 	drm_kms_helper_poll_init(drm);
132*dbf21777SIcenowy Zheng 
133*dbf21777SIcenowy Zheng 	drm_mode_config_reset(drm);
134*dbf21777SIcenowy Zheng 
135*dbf21777SIcenowy Zheng 	ret = drm_dev_register(drm, 0);
136*dbf21777SIcenowy Zheng 	if (ret)
137*dbf21777SIcenowy Zheng 		goto err_fini_poll;
138*dbf21777SIcenowy Zheng 
139*dbf21777SIcenowy Zheng 	drm_client_setup(drm, NULL);
140*dbf21777SIcenowy Zheng 
141*dbf21777SIcenowy Zheng 	return 0;
142*dbf21777SIcenowy Zheng 
143*dbf21777SIcenowy Zheng err_fini_poll:
144*dbf21777SIcenowy Zheng 	drm_kms_helper_poll_fini(drm);
145*dbf21777SIcenowy Zheng 	return ret;
146*dbf21777SIcenowy Zheng }
147*dbf21777SIcenowy Zheng 
148*dbf21777SIcenowy Zheng void vs_drm_finalize(struct vs_dc *dc)
149*dbf21777SIcenowy Zheng {
150*dbf21777SIcenowy Zheng 	struct vs_drm_dev *vdrm = dc->drm_dev;
151*dbf21777SIcenowy Zheng 	struct drm_device *drm = &vdrm->base;
152*dbf21777SIcenowy Zheng 
153*dbf21777SIcenowy Zheng 	drm_dev_unregister(drm);
154*dbf21777SIcenowy Zheng 	drm_kms_helper_poll_fini(drm);
155*dbf21777SIcenowy Zheng 	drm_atomic_helper_shutdown(drm);
156*dbf21777SIcenowy Zheng 	dc->drm_dev = NULL;
157*dbf21777SIcenowy Zheng }
158*dbf21777SIcenowy Zheng 
159*dbf21777SIcenowy Zheng void vs_drm_shutdown_handler(struct vs_dc *dc)
160*dbf21777SIcenowy Zheng {
161*dbf21777SIcenowy Zheng 	struct vs_drm_dev *vdrm = dc->drm_dev;
162*dbf21777SIcenowy Zheng 
163*dbf21777SIcenowy Zheng 	drm_atomic_helper_shutdown(&vdrm->base);
164*dbf21777SIcenowy Zheng }
165*dbf21777SIcenowy Zheng 
166*dbf21777SIcenowy Zheng void vs_drm_handle_irq(struct vs_dc *dc, u32 irqs)
167*dbf21777SIcenowy Zheng {
168*dbf21777SIcenowy Zheng 	unsigned int i;
169*dbf21777SIcenowy Zheng 
170*dbf21777SIcenowy Zheng 	for (i = 0; i < dc->identity.display_count; i++) {
171*dbf21777SIcenowy Zheng 		if (irqs & VSDC_TOP_IRQ_VSYNC(i)) {
172*dbf21777SIcenowy Zheng 			irqs &= ~VSDC_TOP_IRQ_VSYNC(i);
173*dbf21777SIcenowy Zheng 			if (dc->drm_dev->crtcs[i])
174*dbf21777SIcenowy Zheng 				drm_crtc_handle_vblank(&dc->drm_dev->crtcs[i]->base);
175*dbf21777SIcenowy Zheng 		}
176*dbf21777SIcenowy Zheng 	}
177*dbf21777SIcenowy Zheng 
178*dbf21777SIcenowy Zheng 	if (irqs)
179*dbf21777SIcenowy Zheng 		drm_warn_once(&dc->drm_dev->base,
180*dbf21777SIcenowy Zheng 			      "Unknown Verisilicon DC interrupt 0x%x fired!\n",
181*dbf21777SIcenowy Zheng 			      irqs);
182*dbf21777SIcenowy Zheng }
183