Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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
36static 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
50DEFINE_DRM_GEM_FOPS(vs_drm_driver_fops);
51
52static 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
65static 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
71static struct drm_mode_config_helper_funcs vs_mode_config_helper_funcs = {
72 .atomic_commit_tail = drm_atomic_helper_commit_tail,
73};
74
75static 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
85int 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
143err_fini_poll:
144 drm_kms_helper_poll_fini(drm);
145 return ret;
146}
147
148void 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
159void 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
166void 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}