]>
Commit | Line | Data |
---|---|---|
2874c5fd | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
5b809074 NT |
2 | /* |
3 | * Copyright (C) 2016 Noralf Trønnes | |
5b809074 NT |
4 | */ |
5 | ||
0500c04e SR |
6 | #include <linux/module.h> |
7 | #include <linux/slab.h> | |
8 | ||
5b809074 NT |
9 | #include <drm/drm_atomic.h> |
10 | #include <drm/drm_atomic_helper.h> | |
5b809074 | 11 | #include <drm/drm_plane_helper.h> |
fcd70cd3 | 12 | #include <drm/drm_probe_helper.h> |
5b809074 | 13 | #include <drm/drm_simple_kms_helper.h> |
5b809074 NT |
14 | |
15 | /** | |
16 | * DOC: overview | |
17 | * | |
18 | * This helper library provides helpers for drivers for simple display | |
19 | * hardware. | |
20 | * | |
21 | * drm_simple_display_pipe_init() initializes a simple display pipeline | |
22 | * which has only one full-screen scanout buffer feeding one output. The | |
ea0dd85a | 23 | * pipeline is represented by &struct drm_simple_display_pipe and binds |
5b809074 NT |
24 | * together &drm_plane, &drm_crtc and &drm_encoder structures into one fixed |
25 | * entity. Some flexibility for code reuse is provided through a separately | |
26 | * allocated &drm_connector object and supporting optional &drm_bridge | |
27 | * encoder drivers. | |
28 | */ | |
29 | ||
30 | static const struct drm_encoder_funcs drm_simple_kms_encoder_funcs = { | |
31 | .destroy = drm_encoder_cleanup, | |
32 | }; | |
33 | ||
40275dc4 LW |
34 | static enum drm_mode_status |
35 | drm_simple_kms_crtc_mode_valid(struct drm_crtc *crtc, | |
36 | const struct drm_display_mode *mode) | |
37 | { | |
38 | struct drm_simple_display_pipe *pipe; | |
39 | ||
40 | pipe = container_of(crtc, struct drm_simple_display_pipe, crtc); | |
41 | if (!pipe->funcs || !pipe->funcs->mode_valid) | |
42 | /* Anything goes */ | |
43 | return MODE_OK; | |
44 | ||
45 | return pipe->funcs->mode_valid(crtc, mode); | |
46 | } | |
47 | ||
6dcf0de7 DV |
48 | static int drm_simple_kms_crtc_check(struct drm_crtc *crtc, |
49 | struct drm_crtc_state *state) | |
50 | { | |
765831dc | 51 | bool has_primary = state->plane_mask & |
62f77ad0 | 52 | drm_plane_mask(crtc->primary); |
765831dc ML |
53 | |
54 | /* We always want to have an active plane with an active CRTC */ | |
55 | if (has_primary != state->enable) | |
56 | return -EINVAL; | |
57 | ||
6dcf0de7 DV |
58 | return drm_atomic_add_affected_planes(state->state, crtc); |
59 | } | |
60 | ||
0b20a0f8 LP |
61 | static void drm_simple_kms_crtc_enable(struct drm_crtc *crtc, |
62 | struct drm_crtc_state *old_state) | |
5b809074 | 63 | { |
0c9c7fd0 | 64 | struct drm_plane *plane; |
5b809074 NT |
65 | struct drm_simple_display_pipe *pipe; |
66 | ||
67 | pipe = container_of(crtc, struct drm_simple_display_pipe, crtc); | |
68 | if (!pipe->funcs || !pipe->funcs->enable) | |
69 | return; | |
70 | ||
0c9c7fd0 VS |
71 | plane = &pipe->plane; |
72 | pipe->funcs->enable(pipe, crtc->state, plane->state); | |
5b809074 NT |
73 | } |
74 | ||
64581714 LP |
75 | static void drm_simple_kms_crtc_disable(struct drm_crtc *crtc, |
76 | struct drm_crtc_state *old_state) | |
5b809074 NT |
77 | { |
78 | struct drm_simple_display_pipe *pipe; | |
79 | ||
80 | pipe = container_of(crtc, struct drm_simple_display_pipe, crtc); | |
81 | if (!pipe->funcs || !pipe->funcs->disable) | |
82 | return; | |
83 | ||
84 | pipe->funcs->disable(pipe); | |
85 | } | |
86 | ||
87 | static const struct drm_crtc_helper_funcs drm_simple_kms_crtc_helper_funcs = { | |
40275dc4 | 88 | .mode_valid = drm_simple_kms_crtc_mode_valid, |
6dcf0de7 | 89 | .atomic_check = drm_simple_kms_crtc_check, |
0b20a0f8 | 90 | .atomic_enable = drm_simple_kms_crtc_enable, |
64581714 | 91 | .atomic_disable = drm_simple_kms_crtc_disable, |
5b809074 NT |
92 | }; |
93 | ||
ac86cba9 OA |
94 | static int drm_simple_kms_crtc_enable_vblank(struct drm_crtc *crtc) |
95 | { | |
96 | struct drm_simple_display_pipe *pipe; | |
97 | ||
98 | pipe = container_of(crtc, struct drm_simple_display_pipe, crtc); | |
99 | if (!pipe->funcs || !pipe->funcs->enable_vblank) | |
100 | return 0; | |
101 | ||
102 | return pipe->funcs->enable_vblank(pipe); | |
103 | } | |
104 | ||
105 | static void drm_simple_kms_crtc_disable_vblank(struct drm_crtc *crtc) | |
106 | { | |
107 | struct drm_simple_display_pipe *pipe; | |
108 | ||
109 | pipe = container_of(crtc, struct drm_simple_display_pipe, crtc); | |
110 | if (!pipe->funcs || !pipe->funcs->disable_vblank) | |
111 | return; | |
112 | ||
113 | pipe->funcs->disable_vblank(pipe); | |
114 | } | |
115 | ||
5b809074 NT |
116 | static const struct drm_crtc_funcs drm_simple_kms_crtc_funcs = { |
117 | .reset = drm_atomic_helper_crtc_reset, | |
118 | .destroy = drm_crtc_cleanup, | |
119 | .set_config = drm_atomic_helper_set_config, | |
120 | .page_flip = drm_atomic_helper_page_flip, | |
121 | .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, | |
122 | .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, | |
ac86cba9 OA |
123 | .enable_vblank = drm_simple_kms_crtc_enable_vblank, |
124 | .disable_vblank = drm_simple_kms_crtc_disable_vblank, | |
5b809074 NT |
125 | }; |
126 | ||
127 | static int drm_simple_kms_plane_atomic_check(struct drm_plane *plane, | |
128 | struct drm_plane_state *plane_state) | |
129 | { | |
5b809074 NT |
130 | struct drm_simple_display_pipe *pipe; |
131 | struct drm_crtc_state *crtc_state; | |
5b809074 NT |
132 | int ret; |
133 | ||
134 | pipe = container_of(plane, struct drm_simple_display_pipe, plane); | |
b4d93679 ML |
135 | crtc_state = drm_atomic_get_new_crtc_state(plane_state->state, |
136 | &pipe->crtc); | |
4be12cc2 | 137 | |
a01cb8ba | 138 | ret = drm_atomic_helper_check_plane_state(plane_state, crtc_state, |
a01cb8ba VS |
139 | DRM_PLANE_HELPER_NO_SCALING, |
140 | DRM_PLANE_HELPER_NO_SCALING, | |
141 | false, true); | |
5b809074 NT |
142 | if (ret) |
143 | return ret; | |
144 | ||
4be12cc2 | 145 | if (!plane_state->visible) |
4751cf73 OA |
146 | return 0; |
147 | ||
5b809074 NT |
148 | if (!pipe->funcs || !pipe->funcs->check) |
149 | return 0; | |
150 | ||
151 | return pipe->funcs->check(pipe, plane_state, crtc_state); | |
152 | } | |
153 | ||
154 | static void drm_simple_kms_plane_atomic_update(struct drm_plane *plane, | |
bcd2ba02 | 155 | struct drm_plane_state *old_pstate) |
5b809074 NT |
156 | { |
157 | struct drm_simple_display_pipe *pipe; | |
158 | ||
159 | pipe = container_of(plane, struct drm_simple_display_pipe, plane); | |
160 | if (!pipe->funcs || !pipe->funcs->update) | |
161 | return; | |
162 | ||
bcd2ba02 | 163 | pipe->funcs->update(pipe, old_pstate); |
5b809074 NT |
164 | } |
165 | ||
7d83a155 MV |
166 | static int drm_simple_kms_plane_prepare_fb(struct drm_plane *plane, |
167 | struct drm_plane_state *state) | |
168 | { | |
169 | struct drm_simple_display_pipe *pipe; | |
170 | ||
171 | pipe = container_of(plane, struct drm_simple_display_pipe, plane); | |
172 | if (!pipe->funcs || !pipe->funcs->prepare_fb) | |
173 | return 0; | |
174 | ||
175 | return pipe->funcs->prepare_fb(pipe, state); | |
176 | } | |
177 | ||
178 | static void drm_simple_kms_plane_cleanup_fb(struct drm_plane *plane, | |
179 | struct drm_plane_state *state) | |
180 | { | |
181 | struct drm_simple_display_pipe *pipe; | |
182 | ||
183 | pipe = container_of(plane, struct drm_simple_display_pipe, plane); | |
184 | if (!pipe->funcs || !pipe->funcs->cleanup_fb) | |
185 | return; | |
186 | ||
187 | pipe->funcs->cleanup_fb(pipe, state); | |
188 | } | |
189 | ||
dff906c3 EA |
190 | static bool drm_simple_kms_format_mod_supported(struct drm_plane *plane, |
191 | uint32_t format, | |
192 | uint64_t modifier) | |
193 | { | |
194 | return modifier == DRM_FORMAT_MOD_LINEAR; | |
195 | } | |
196 | ||
5b809074 | 197 | static const struct drm_plane_helper_funcs drm_simple_kms_plane_helper_funcs = { |
7d83a155 MV |
198 | .prepare_fb = drm_simple_kms_plane_prepare_fb, |
199 | .cleanup_fb = drm_simple_kms_plane_cleanup_fb, | |
5b809074 NT |
200 | .atomic_check = drm_simple_kms_plane_atomic_check, |
201 | .atomic_update = drm_simple_kms_plane_atomic_update, | |
202 | }; | |
203 | ||
204 | static const struct drm_plane_funcs drm_simple_kms_plane_funcs = { | |
205 | .update_plane = drm_atomic_helper_update_plane, | |
206 | .disable_plane = drm_atomic_helper_disable_plane, | |
207 | .destroy = drm_plane_cleanup, | |
208 | .reset = drm_atomic_helper_plane_reset, | |
209 | .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, | |
210 | .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, | |
dff906c3 | 211 | .format_mod_supported = drm_simple_kms_format_mod_supported, |
5b809074 NT |
212 | }; |
213 | ||
315486c6 AM |
214 | /** |
215 | * drm_simple_display_pipe_attach_bridge - Attach a bridge to the display pipe | |
216 | * @pipe: simple display pipe object | |
217 | * @bridge: bridge to attach | |
218 | * | |
219 | * Makes it possible to still use the drm_simple_display_pipe helpers when | |
220 | * a DRM bridge has to be used. | |
221 | * | |
222 | * Note that you probably want to initialize the pipe by passing a NULL | |
223 | * connector to drm_simple_display_pipe_init(). | |
224 | * | |
225 | * Returns: | |
226 | * Zero on success, negative error code on failure. | |
227 | */ | |
228 | int drm_simple_display_pipe_attach_bridge(struct drm_simple_display_pipe *pipe, | |
229 | struct drm_bridge *bridge) | |
230 | { | |
3bb80f24 | 231 | return drm_bridge_attach(&pipe->encoder, bridge, NULL); |
315486c6 AM |
232 | } |
233 | EXPORT_SYMBOL(drm_simple_display_pipe_attach_bridge); | |
234 | ||
5b809074 NT |
235 | /** |
236 | * drm_simple_display_pipe_init - Initialize a simple display pipeline | |
237 | * @dev: DRM device | |
238 | * @pipe: simple display pipe object to initialize | |
239 | * @funcs: callbacks for the display pipe (optional) | |
62cacc79 | 240 | * @formats: array of supported formats (DRM_FORMAT\_\*) |
5b809074 | 241 | * @format_count: number of elements in @formats |
e6fc3b68 | 242 | * @format_modifiers: array of formats modifiers |
4f993973 | 243 | * @connector: connector to attach and register (optional) |
5b809074 NT |
244 | * |
245 | * Sets up a display pipeline which consist of a really simple | |
4f993973 AM |
246 | * plane-crtc-encoder pipe. |
247 | * | |
248 | * If a connector is supplied, the pipe will be coupled with the provided | |
249 | * connector. You may supply a NULL connector when using drm bridges, that | |
250 | * handle connectors themselves (see drm_simple_display_pipe_attach_bridge()). | |
251 | * | |
5b809074 NT |
252 | * Teardown of a simple display pipe is all handled automatically by the drm |
253 | * core through calling drm_mode_config_cleanup(). Drivers afterwards need to | |
254 | * release the memory for the structure themselves. | |
255 | * | |
256 | * Returns: | |
257 | * Zero on success, negative error code on failure. | |
258 | */ | |
259 | int drm_simple_display_pipe_init(struct drm_device *dev, | |
260 | struct drm_simple_display_pipe *pipe, | |
261 | const struct drm_simple_display_pipe_funcs *funcs, | |
262 | const uint32_t *formats, unsigned int format_count, | |
e6fc3b68 | 263 | const uint64_t *format_modifiers, |
5b809074 NT |
264 | struct drm_connector *connector) |
265 | { | |
266 | struct drm_encoder *encoder = &pipe->encoder; | |
267 | struct drm_plane *plane = &pipe->plane; | |
268 | struct drm_crtc *crtc = &pipe->crtc; | |
269 | int ret; | |
270 | ||
271 | pipe->connector = connector; | |
272 | pipe->funcs = funcs; | |
273 | ||
274 | drm_plane_helper_add(plane, &drm_simple_kms_plane_helper_funcs); | |
275 | ret = drm_universal_plane_init(dev, plane, 0, | |
276 | &drm_simple_kms_plane_funcs, | |
277 | formats, format_count, | |
e6fc3b68 | 278 | format_modifiers, |
5b809074 NT |
279 | DRM_PLANE_TYPE_PRIMARY, NULL); |
280 | if (ret) | |
281 | return ret; | |
282 | ||
283 | drm_crtc_helper_add(crtc, &drm_simple_kms_crtc_helper_funcs); | |
284 | ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL, | |
285 | &drm_simple_kms_crtc_funcs, NULL); | |
286 | if (ret) | |
287 | return ret; | |
288 | ||
6a52193b | 289 | encoder->possible_crtcs = drm_crtc_mask(crtc); |
5b809074 NT |
290 | ret = drm_encoder_init(dev, encoder, &drm_simple_kms_encoder_funcs, |
291 | DRM_MODE_ENCODER_NONE, NULL); | |
4f993973 | 292 | if (ret || !connector) |
5b809074 NT |
293 | return ret; |
294 | ||
cde4c44d | 295 | return drm_connector_attach_encoder(connector, encoder); |
5b809074 NT |
296 | } |
297 | EXPORT_SYMBOL(drm_simple_display_pipe_init); | |
298 | ||
299 | MODULE_LICENSE("GPL"); |