1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright (C) 2020 Unisoc Inc. |
4 | */ |
5 | |
6 | #include <linux/component.h> |
7 | #include <linux/dma-mapping.h> |
8 | #include <linux/mod_devicetable.h> |
9 | #include <linux/module.h> |
10 | #include <linux/mutex.h> |
11 | #include <linux/of_graph.h> |
12 | #include <linux/platform_device.h> |
13 | |
14 | #include <drm/drm_atomic_helper.h> |
15 | #include <drm/drm_drv.h> |
16 | #include <drm/drm_gem_dma_helper.h> |
17 | #include <drm/drm_gem_framebuffer_helper.h> |
18 | #include <drm/drm_of.h> |
19 | #include <drm/drm_probe_helper.h> |
20 | #include <drm/drm_vblank.h> |
21 | |
22 | #include "sprd_drm.h" |
23 | |
24 | #define DRIVER_NAME "sprd" |
25 | #define DRIVER_DESC "Spreadtrum SoCs' DRM Driver" |
26 | #define DRIVER_DATE "20200201" |
27 | #define DRIVER_MAJOR 1 |
28 | #define DRIVER_MINOR 0 |
29 | |
30 | static const struct drm_mode_config_helper_funcs sprd_drm_mode_config_helper = { |
31 | .atomic_commit_tail = drm_atomic_helper_commit_tail_rpm, |
32 | }; |
33 | |
34 | static const struct drm_mode_config_funcs sprd_drm_mode_config_funcs = { |
35 | .fb_create = drm_gem_fb_create, |
36 | .atomic_check = drm_atomic_helper_check, |
37 | .atomic_commit = drm_atomic_helper_commit, |
38 | }; |
39 | |
40 | static void sprd_drm_mode_config_init(struct drm_device *drm) |
41 | { |
42 | drm->mode_config.min_width = 0; |
43 | drm->mode_config.min_height = 0; |
44 | drm->mode_config.max_width = 8192; |
45 | drm->mode_config.max_height = 8192; |
46 | |
47 | drm->mode_config.funcs = &sprd_drm_mode_config_funcs; |
48 | drm->mode_config.helper_private = &sprd_drm_mode_config_helper; |
49 | } |
50 | |
51 | DEFINE_DRM_GEM_DMA_FOPS(sprd_drm_fops); |
52 | |
53 | static struct drm_driver sprd_drm_drv = { |
54 | .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, |
55 | .fops = &sprd_drm_fops, |
56 | |
57 | /* GEM Operations */ |
58 | DRM_GEM_DMA_DRIVER_OPS, |
59 | |
60 | .name = DRIVER_NAME, |
61 | .desc = DRIVER_DESC, |
62 | .date = DRIVER_DATE, |
63 | .major = DRIVER_MAJOR, |
64 | .minor = DRIVER_MINOR, |
65 | }; |
66 | |
67 | static int sprd_drm_bind(struct device *dev) |
68 | { |
69 | struct platform_device *pdev = to_platform_device(dev); |
70 | struct drm_device *drm; |
71 | struct sprd_drm *sprd; |
72 | int ret; |
73 | |
74 | sprd = devm_drm_dev_alloc(dev, &sprd_drm_drv, struct sprd_drm, drm); |
75 | if (IS_ERR(ptr: sprd)) |
76 | return PTR_ERR(ptr: sprd); |
77 | |
78 | drm = &sprd->drm; |
79 | platform_set_drvdata(pdev, data: drm); |
80 | |
81 | ret = drmm_mode_config_init(dev: drm); |
82 | if (ret) |
83 | return ret; |
84 | |
85 | sprd_drm_mode_config_init(drm); |
86 | |
87 | /* bind and init sub drivers */ |
88 | ret = component_bind_all(parent: drm->dev, data: drm); |
89 | if (ret) { |
90 | drm_err(drm, "failed to bind all component.\n" ); |
91 | return ret; |
92 | } |
93 | |
94 | /* vblank init */ |
95 | ret = drm_vblank_init(dev: drm, num_crtcs: drm->mode_config.num_crtc); |
96 | if (ret) { |
97 | drm_err(drm, "failed to initialize vblank.\n" ); |
98 | goto err_unbind_all; |
99 | } |
100 | |
101 | /* reset all the states of crtc/plane/encoder/connector */ |
102 | drm_mode_config_reset(dev: drm); |
103 | |
104 | /* init kms poll for handling hpd */ |
105 | drm_kms_helper_poll_init(dev: drm); |
106 | |
107 | ret = drm_dev_register(dev: drm, flags: 0); |
108 | if (ret < 0) |
109 | goto err_kms_helper_poll_fini; |
110 | |
111 | return 0; |
112 | |
113 | err_kms_helper_poll_fini: |
114 | drm_kms_helper_poll_fini(dev: drm); |
115 | err_unbind_all: |
116 | component_unbind_all(parent: drm->dev, data: drm); |
117 | return ret; |
118 | } |
119 | |
120 | static void sprd_drm_unbind(struct device *dev) |
121 | { |
122 | struct drm_device *drm = dev_get_drvdata(dev); |
123 | |
124 | drm_dev_unregister(dev: drm); |
125 | |
126 | drm_kms_helper_poll_fini(dev: drm); |
127 | |
128 | component_unbind_all(parent: drm->dev, data: drm); |
129 | } |
130 | |
131 | static const struct component_master_ops drm_component_ops = { |
132 | .bind = sprd_drm_bind, |
133 | .unbind = sprd_drm_unbind, |
134 | }; |
135 | |
136 | static int sprd_drm_probe(struct platform_device *pdev) |
137 | { |
138 | return drm_of_component_probe(dev: &pdev->dev, compare_of: component_compare_of, m_ops: &drm_component_ops); |
139 | } |
140 | |
141 | static void sprd_drm_remove(struct platform_device *pdev) |
142 | { |
143 | component_master_del(&pdev->dev, &drm_component_ops); |
144 | } |
145 | |
146 | static void sprd_drm_shutdown(struct platform_device *pdev) |
147 | { |
148 | struct drm_device *drm = platform_get_drvdata(pdev); |
149 | |
150 | if (!drm) { |
151 | dev_warn(&pdev->dev, "drm device is not available, no shutdown\n" ); |
152 | return; |
153 | } |
154 | |
155 | drm_atomic_helper_shutdown(dev: drm); |
156 | } |
157 | |
158 | static const struct of_device_id drm_match_table[] = { |
159 | { .compatible = "sprd,display-subsystem" , }, |
160 | { /* sentinel */ }, |
161 | }; |
162 | MODULE_DEVICE_TABLE(of, drm_match_table); |
163 | |
164 | static struct platform_driver sprd_drm_driver = { |
165 | .probe = sprd_drm_probe, |
166 | .remove_new = sprd_drm_remove, |
167 | .shutdown = sprd_drm_shutdown, |
168 | .driver = { |
169 | .name = "sprd-drm-drv" , |
170 | .of_match_table = drm_match_table, |
171 | }, |
172 | }; |
173 | |
174 | static struct platform_driver *sprd_drm_drivers[] = { |
175 | &sprd_drm_driver, |
176 | &sprd_dpu_driver, |
177 | &sprd_dsi_driver, |
178 | }; |
179 | |
180 | static int __init sprd_drm_init(void) |
181 | { |
182 | if (drm_firmware_drivers_only()) |
183 | return -ENODEV; |
184 | |
185 | return platform_register_drivers(sprd_drm_drivers, |
186 | ARRAY_SIZE(sprd_drm_drivers)); |
187 | } |
188 | |
189 | static void __exit sprd_drm_exit(void) |
190 | { |
191 | platform_unregister_drivers(drivers: sprd_drm_drivers, |
192 | ARRAY_SIZE(sprd_drm_drivers)); |
193 | } |
194 | |
195 | module_init(sprd_drm_init); |
196 | module_exit(sprd_drm_exit); |
197 | |
198 | MODULE_AUTHOR("Leon He <leon.he@unisoc.com>" ); |
199 | MODULE_AUTHOR("Kevin Tang <kevin.tang@unisoc.com>" ); |
200 | MODULE_DESCRIPTION("Unisoc DRM KMS Master Driver" ); |
201 | MODULE_LICENSE("GPL v2" ); |
202 | |