Commit | Line | Data |
---|---|---|
c103d1cf MR |
1 | /* |
2 | * Copyright (C) 2014 Intel Corporation | |
3 | * | |
4 | * DRM universal plane helper functions | |
5 | * | |
6 | * Permission is hereby granted, free of charge, to any person obtaining a | |
7 | * copy of this software and associated documentation files (the "Software"), | |
8 | * to deal in the Software without restriction, including without limitation | |
9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
10 | * and/or sell copies of the Software, and to permit persons to whom the | |
11 | * Software is furnished to do so, subject to the following conditions: | |
12 | * | |
13 | * The above copyright notice and this permission notice (including the next | |
14 | * paragraph) shall be included in all copies or substantial portions of the | |
15 | * Software. | |
16 | * | |
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
20 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
23 | * SOFTWARE. | |
24 | */ | |
25 | ||
26 | #include <linux/list.h> | |
27 | #include <drm/drmP.h> | |
7daf8d54 | 28 | #include <drm/drm_plane_helper.h> |
c103d1cf | 29 | #include <drm/drm_rect.h> |
f220e626 | 30 | #include <drm/drm_plane_helper.h> |
c103d1cf MR |
31 | |
32 | #define SUBPIXEL_MASK 0xffff | |
33 | ||
34 | /* | |
35 | * This is the minimal list of formats that seem to be safe for modeset use | |
36 | * with all current DRM drivers. Most hardware can actually support more | |
37 | * formats than this and drivers may specify a more accurate list when | |
38 | * creating the primary plane. However drivers that still call | |
39 | * drm_plane_init() will use this minimal format list as the default. | |
40 | */ | |
233fd4ec TR |
41 | static const uint32_t safe_modeset_formats[] = { |
42 | DRM_FORMAT_XRGB8888, | |
43 | DRM_FORMAT_ARGB8888, | |
c103d1cf MR |
44 | }; |
45 | ||
46 | /* | |
47 | * Returns the connectors currently associated with a CRTC. This function | |
48 | * should be called twice: once with a NULL connector list to retrieve | |
49 | * the list size, and once with the properly allocated list to be filled in. | |
50 | */ | |
51 | static int get_connectors_for_crtc(struct drm_crtc *crtc, | |
52 | struct drm_connector **connector_list, | |
53 | int num_connectors) | |
54 | { | |
55 | struct drm_device *dev = crtc->dev; | |
56 | struct drm_connector *connector; | |
57 | int count = 0; | |
58 | ||
6e9f798d DV |
59 | /* |
60 | * Note: Once we change the plane hooks to more fine-grained locking we | |
61 | * need to grab the connection_mutex here to be able to make these | |
62 | * checks. | |
63 | */ | |
51fd371b | 64 | WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex)); |
6e9f798d | 65 | |
c103d1cf MR |
66 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) |
67 | if (connector->encoder && connector->encoder->crtc == crtc) { | |
68 | if (connector_list != NULL && count < num_connectors) | |
69 | *(connector_list++) = connector; | |
70 | ||
71 | count++; | |
72 | } | |
73 | ||
74 | return count; | |
75 | } | |
76 | ||
7daf8d54 MR |
77 | /** |
78 | * drm_plane_helper_check_update() - Check plane update for validity | |
79 | * @plane: plane object to update | |
80 | * @crtc: owning CRTC of owning plane | |
81 | * @fb: framebuffer to flip onto plane | |
82 | * @src: source coordinates in 16.16 fixed point | |
83 | * @dest: integer destination coordinates | |
84 | * @clip: integer clipping coordinates | |
85 | * @min_scale: minimum @src:@dest scaling factor in 16.16 fixed point | |
86 | * @max_scale: maximum @src:@dest scaling factor in 16.16 fixed point | |
87 | * @can_position: is it legal to position the plane such that it | |
88 | * doesn't cover the entire crtc? This will generally | |
89 | * only be false for primary planes. | |
90 | * @can_update_disabled: can the plane be updated while the crtc | |
91 | * is disabled? | |
92 | * @visible: output parameter indicating whether plane is still visible after | |
93 | * clipping | |
94 | * | |
95 | * Checks that a desired plane update is valid. Drivers that provide | |
96 | * their own plane handling rather than helper-provided implementations may | |
97 | * still wish to call this function to avoid duplication of error checking | |
98 | * code. | |
99 | * | |
100 | * RETURNS: | |
101 | * Zero if update appears valid, error code on failure | |
102 | */ | |
103 | int drm_plane_helper_check_update(struct drm_plane *plane, | |
104 | struct drm_crtc *crtc, | |
105 | struct drm_framebuffer *fb, | |
106 | struct drm_rect *src, | |
107 | struct drm_rect *dest, | |
108 | const struct drm_rect *clip, | |
109 | int min_scale, | |
110 | int max_scale, | |
111 | bool can_position, | |
112 | bool can_update_disabled, | |
113 | bool *visible) | |
114 | { | |
115 | int hscale, vscale; | |
116 | ||
117 | if (!crtc->enabled && !can_update_disabled) { | |
118 | DRM_DEBUG_KMS("Cannot update plane of a disabled CRTC.\n"); | |
119 | return -EINVAL; | |
120 | } | |
121 | ||
122 | /* Check scaling */ | |
123 | hscale = drm_rect_calc_hscale(src, dest, min_scale, max_scale); | |
124 | vscale = drm_rect_calc_vscale(src, dest, min_scale, max_scale); | |
125 | if (hscale < 0 || vscale < 0) { | |
126 | DRM_DEBUG_KMS("Invalid scaling of plane\n"); | |
127 | return -ERANGE; | |
128 | } | |
129 | ||
130 | *visible = drm_rect_clip_scaled(src, dest, clip, hscale, vscale); | |
131 | if (!*visible) | |
132 | /* | |
133 | * Plane isn't visible; some drivers can handle this | |
134 | * so we just return success here. Drivers that can't | |
135 | * (including those that use the primary plane helper's | |
136 | * update function) will return an error from their | |
137 | * update_plane handler. | |
138 | */ | |
139 | return 0; | |
140 | ||
141 | if (!can_position && !drm_rect_equals(dest, clip)) { | |
142 | DRM_DEBUG_KMS("Plane must cover entire CRTC\n"); | |
143 | return -EINVAL; | |
144 | } | |
145 | ||
146 | return 0; | |
147 | } | |
148 | EXPORT_SYMBOL(drm_plane_helper_check_update); | |
149 | ||
c103d1cf MR |
150 | /** |
151 | * drm_primary_helper_update() - Helper for primary plane update | |
152 | * @plane: plane object to update | |
153 | * @crtc: owning CRTC of owning plane | |
154 | * @fb: framebuffer to flip onto plane | |
155 | * @crtc_x: x offset of primary plane on crtc | |
156 | * @crtc_y: y offset of primary plane on crtc | |
157 | * @crtc_w: width of primary plane rectangle on crtc | |
158 | * @crtc_h: height of primary plane rectangle on crtc | |
159 | * @src_x: x offset of @fb for panning | |
160 | * @src_y: y offset of @fb for panning | |
161 | * @src_w: width of source rectangle in @fb | |
162 | * @src_h: height of source rectangle in @fb | |
163 | * | |
164 | * Provides a default plane update handler for primary planes. This is handler | |
165 | * is called in response to a userspace SetPlane operation on the plane with a | |
166 | * non-NULL framebuffer. We call the driver's modeset handler to update the | |
167 | * framebuffer. | |
168 | * | |
169 | * SetPlane() on a primary plane of a disabled CRTC is not supported, and will | |
170 | * return an error. | |
171 | * | |
172 | * Note that we make some assumptions about hardware limitations that may not be | |
173 | * true for all hardware -- | |
174 | * 1) Primary plane cannot be repositioned. | |
175 | * 2) Primary plane cannot be scaled. | |
176 | * 3) Primary plane must cover the entire CRTC. | |
177 | * 4) Subpixel positioning is not supported. | |
178 | * Drivers for hardware that don't have these restrictions can provide their | |
179 | * own implementation rather than using this helper. | |
180 | * | |
181 | * RETURNS: | |
182 | * Zero on success, error code on failure | |
183 | */ | |
184 | int drm_primary_helper_update(struct drm_plane *plane, struct drm_crtc *crtc, | |
185 | struct drm_framebuffer *fb, | |
186 | int crtc_x, int crtc_y, | |
187 | unsigned int crtc_w, unsigned int crtc_h, | |
188 | uint32_t src_x, uint32_t src_y, | |
189 | uint32_t src_w, uint32_t src_h) | |
190 | { | |
191 | struct drm_mode_set set = { | |
192 | .crtc = crtc, | |
193 | .fb = fb, | |
194 | .mode = &crtc->mode, | |
195 | .x = src_x >> 16, | |
196 | .y = src_y >> 16, | |
197 | }; | |
7daf8d54 MR |
198 | struct drm_rect src = { |
199 | .x1 = src_x, | |
200 | .y1 = src_y, | |
201 | .x2 = src_x + src_w, | |
202 | .y2 = src_y + src_h, | |
203 | }; | |
c103d1cf MR |
204 | struct drm_rect dest = { |
205 | .x1 = crtc_x, | |
206 | .y1 = crtc_y, | |
207 | .x2 = crtc_x + crtc_w, | |
208 | .y2 = crtc_y + crtc_h, | |
209 | }; | |
7daf8d54 | 210 | const struct drm_rect clip = { |
c103d1cf MR |
211 | .x2 = crtc->mode.hdisplay, |
212 | .y2 = crtc->mode.vdisplay, | |
213 | }; | |
214 | struct drm_connector **connector_list; | |
c103d1cf | 215 | int num_connectors, ret; |
7daf8d54 | 216 | bool visible; |
c103d1cf | 217 | |
7daf8d54 MR |
218 | ret = drm_plane_helper_check_update(plane, crtc, fb, |
219 | &src, &dest, &clip, | |
220 | DRM_PLANE_HELPER_NO_SCALING, | |
221 | DRM_PLANE_HELPER_NO_SCALING, | |
222 | false, false, &visible); | |
c103d1cf MR |
223 | if (ret) |
224 | return ret; | |
225 | ||
7daf8d54 MR |
226 | if (!visible) |
227 | /* | |
228 | * Primary plane isn't visible. Note that unless a driver | |
229 | * provides their own disable function, this will just | |
230 | * wind up returning -EINVAL to userspace. | |
231 | */ | |
232 | return plane->funcs->disable_plane(plane); | |
233 | ||
c103d1cf MR |
234 | /* Find current connectors for CRTC */ |
235 | num_connectors = get_connectors_for_crtc(crtc, NULL, 0); | |
236 | BUG_ON(num_connectors == 0); | |
237 | connector_list = kzalloc(num_connectors * sizeof(*connector_list), | |
238 | GFP_KERNEL); | |
239 | if (!connector_list) | |
240 | return -ENOMEM; | |
241 | get_connectors_for_crtc(crtc, connector_list, num_connectors); | |
242 | ||
243 | set.connectors = connector_list; | |
244 | set.num_connectors = num_connectors; | |
245 | ||
246 | /* | |
0fe27f06 | 247 | * We call set_config() directly here rather than using |
c103d1cf MR |
248 | * drm_mode_set_config_internal. We're reprogramming the same |
249 | * connectors that were already in use, so we shouldn't need the extra | |
250 | * cross-CRTC fb refcounting to accomodate stealing connectors. | |
251 | * drm_mode_setplane() already handles the basic refcounting for the | |
252 | * framebuffers involved in this operation. | |
253 | */ | |
c103d1cf | 254 | ret = crtc->funcs->set_config(&set); |
c103d1cf MR |
255 | |
256 | kfree(connector_list); | |
257 | return ret; | |
258 | } | |
259 | EXPORT_SYMBOL(drm_primary_helper_update); | |
260 | ||
261 | /** | |
262 | * drm_primary_helper_disable() - Helper for primary plane disable | |
263 | * @plane: plane to disable | |
264 | * | |
265 | * Provides a default plane disable handler for primary planes. This is handler | |
266 | * is called in response to a userspace SetPlane operation on the plane with a | |
b6ccd7b9 DV |
267 | * NULL framebuffer parameter. It unconditionally fails the disable call with |
268 | * -EINVAL the only way to disable the primary plane without driver support is | |
269 | * to disable the entier CRTC. Which does not match the plane ->disable hook. | |
c103d1cf MR |
270 | * |
271 | * Note that some hardware may be able to disable the primary plane without | |
272 | * disabling the whole CRTC. Drivers for such hardware should provide their | |
273 | * own disable handler that disables just the primary plane (and they'll likely | |
274 | * need to provide their own update handler as well to properly re-enable a | |
275 | * disabled primary plane). | |
276 | * | |
277 | * RETURNS: | |
b6ccd7b9 | 278 | * Unconditionally returns -EINVAL. |
c103d1cf MR |
279 | */ |
280 | int drm_primary_helper_disable(struct drm_plane *plane) | |
281 | { | |
b6ccd7b9 | 282 | return -EINVAL; |
c103d1cf MR |
283 | } |
284 | EXPORT_SYMBOL(drm_primary_helper_disable); | |
285 | ||
286 | /** | |
287 | * drm_primary_helper_destroy() - Helper for primary plane destruction | |
288 | * @plane: plane to destroy | |
289 | * | |
290 | * Provides a default plane destroy handler for primary planes. This handler | |
291 | * is called during CRTC destruction. We disable the primary plane, remove | |
292 | * it from the DRM plane list, and deallocate the plane structure. | |
293 | */ | |
294 | void drm_primary_helper_destroy(struct drm_plane *plane) | |
295 | { | |
c103d1cf MR |
296 | drm_plane_cleanup(plane); |
297 | kfree(plane); | |
298 | } | |
299 | EXPORT_SYMBOL(drm_primary_helper_destroy); | |
300 | ||
301 | const struct drm_plane_funcs drm_primary_helper_funcs = { | |
302 | .update_plane = drm_primary_helper_update, | |
303 | .disable_plane = drm_primary_helper_disable, | |
304 | .destroy = drm_primary_helper_destroy, | |
305 | }; | |
306 | EXPORT_SYMBOL(drm_primary_helper_funcs); | |
307 | ||
308 | /** | |
309 | * drm_primary_helper_create_plane() - Create a generic primary plane | |
310 | * @dev: drm device | |
311 | * @formats: pixel formats supported, or NULL for a default safe list | |
312 | * @num_formats: size of @formats; ignored if @formats is NULL | |
313 | * | |
314 | * Allocates and initializes a primary plane that can be used with the primary | |
315 | * plane helpers. Drivers that wish to use driver-specific plane structures or | |
316 | * provide custom handler functions may perform their own allocation and | |
317 | * initialization rather than calling this function. | |
318 | */ | |
319 | struct drm_plane *drm_primary_helper_create_plane(struct drm_device *dev, | |
320 | const uint32_t *formats, | |
321 | int num_formats) | |
322 | { | |
323 | struct drm_plane *primary; | |
324 | int ret; | |
325 | ||
326 | primary = kzalloc(sizeof(*primary), GFP_KERNEL); | |
327 | if (primary == NULL) { | |
328 | DRM_DEBUG_KMS("Failed to allocate primary plane\n"); | |
329 | return NULL; | |
330 | } | |
331 | ||
332 | if (formats == NULL) { | |
333 | formats = safe_modeset_formats; | |
334 | num_formats = ARRAY_SIZE(safe_modeset_formats); | |
335 | } | |
336 | ||
337 | /* possible_crtc's will be filled in later by crtc_init */ | |
338 | ret = drm_plane_init(dev, primary, 0, &drm_primary_helper_funcs, | |
339 | formats, num_formats, | |
340 | DRM_PLANE_TYPE_PRIMARY); | |
341 | if (ret) { | |
342 | kfree(primary); | |
343 | primary = NULL; | |
344 | } | |
345 | ||
346 | return primary; | |
347 | } | |
348 | EXPORT_SYMBOL(drm_primary_helper_create_plane); | |
349 | ||
e13161af MR |
350 | /** |
351 | * drm_crtc_init - Legacy CRTC initialization function | |
352 | * @dev: DRM device | |
353 | * @crtc: CRTC object to init | |
354 | * @funcs: callbacks for the new CRTC | |
355 | * | |
356 | * Initialize a CRTC object with a default helper-provided primary plane and no | |
357 | * cursor plane. | |
358 | * | |
359 | * Returns: | |
360 | * Zero on success, error code on failure. | |
361 | */ | |
362 | int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, | |
363 | const struct drm_crtc_funcs *funcs) | |
364 | { | |
365 | struct drm_plane *primary; | |
366 | ||
367 | primary = drm_primary_helper_create_plane(dev, NULL, 0); | |
368 | return drm_crtc_init_with_planes(dev, crtc, primary, NULL, funcs); | |
369 | } | |
370 | EXPORT_SYMBOL(drm_crtc_init); |