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