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 <linux/export.h> |
28 | #include <drm/drmP.h> |
29 | #include <drm/drm_rect.h> |
30 | #include <drm/drm_plane_helper.h> |
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 | */ |
41 | static const uint32_t safe_modeset_formats[] = { |
42 | DRM_FORMAT_XRGB8888, |
43 | DRM_FORMAT_ARGB8888, |
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 | |
59 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) |
60 | if (connector->encoder && connector->encoder->crtc == crtc) { |
61 | if (connector_list != NULL && count < num_connectors) |
62 | *(connector_list++) = connector; |
63 | |
64 | count++; |
65 | } |
66 | |
67 | return count; |
68 | } |
69 | |
70 | /** |
71 | * drm_primary_helper_update() - Helper for primary plane update |
72 | * @plane: plane object to update |
73 | * @crtc: owning CRTC of owning plane |
74 | * @fb: framebuffer to flip onto plane |
75 | * @crtc_x: x offset of primary plane on crtc |
76 | * @crtc_y: y offset of primary plane on crtc |
77 | * @crtc_w: width of primary plane rectangle on crtc |
78 | * @crtc_h: height of primary plane rectangle on crtc |
79 | * @src_x: x offset of @fb for panning |
80 | * @src_y: y offset of @fb for panning |
81 | * @src_w: width of source rectangle in @fb |
82 | * @src_h: height of source rectangle in @fb |
83 | * |
84 | * Provides a default plane update handler for primary planes. This is handler |
85 | * is called in response to a userspace SetPlane operation on the plane with a |
86 | * non-NULL framebuffer. We call the driver's modeset handler to update the |
87 | * framebuffer. |
88 | * |
89 | * SetPlane() on a primary plane of a disabled CRTC is not supported, and will |
90 | * return an error. |
91 | * |
92 | * Note that we make some assumptions about hardware limitations that may not be |
93 | * true for all hardware -- |
94 | * 1) Primary plane cannot be repositioned. |
95 | * 2) Primary plane cannot be scaled. |
96 | * 3) Primary plane must cover the entire CRTC. |
97 | * 4) Subpixel positioning is not supported. |
98 | * Drivers for hardware that don't have these restrictions can provide their |
99 | * own implementation rather than using this helper. |
100 | * |
101 | * RETURNS: |
102 | * Zero on success, error code on failure |
103 | */ |
104 | int drm_primary_helper_update(struct drm_plane *plane, struct drm_crtc *crtc, |
105 | struct drm_framebuffer *fb, |
106 | int crtc_x, int crtc_y, |
107 | unsigned int crtc_w, unsigned int crtc_h, |
108 | uint32_t src_x, uint32_t src_y, |
109 | uint32_t src_w, uint32_t src_h) |
110 | { |
111 | struct drm_mode_set set = { |
112 | .crtc = crtc, |
113 | .fb = fb, |
114 | .mode = &crtc->mode, |
115 | .x = src_x >> 16, |
116 | .y = src_y >> 16, |
117 | }; |
118 | struct drm_rect dest = { |
119 | .x1 = crtc_x, |
120 | .y1 = crtc_y, |
121 | .x2 = crtc_x + crtc_w, |
122 | .y2 = crtc_y + crtc_h, |
123 | }; |
124 | struct drm_rect clip = { |
125 | .x2 = crtc->mode.hdisplay, |
126 | .y2 = crtc->mode.vdisplay, |
127 | }; |
128 | struct drm_connector **connector_list; |
129 | struct drm_framebuffer *tmpfb; |
130 | int num_connectors, ret; |
131 | |
132 | if (!crtc->enabled) { |
133 | DRM_DEBUG_KMS("Cannot update primary plane of a disabled CRTC.\n" ); |
134 | return -EINVAL; |
135 | } |
136 | |
137 | /* Disallow subpixel positioning */ |
138 | if ((src_x | src_y | src_w | src_h) & SUBPIXEL_MASK) { |
139 | DRM_DEBUG_KMS("Primary plane does not support subpixel positioning\n" ); |
140 | return -EINVAL; |
141 | } |
142 | |
143 | /* Primary planes are locked to their owning CRTC */ |
144 | if (plane->possible_crtcs != drm_crtc_mask(crtc)) { |
145 | DRM_DEBUG_KMS("Cannot change primary plane CRTC\n" ); |
146 | return -EINVAL; |
147 | } |
148 | |
149 | /* Disallow scaling */ |
150 | if (crtc_w != src_w || crtc_h != src_h) { |
151 | DRM_DEBUG_KMS("Can't scale primary plane\n" ); |
152 | return -EINVAL; |
153 | } |
154 | |
155 | /* Make sure primary plane covers entire CRTC */ |
156 | drm_rect_intersect(&dest, &clip); |
157 | if (dest.x1 != 0 || dest.y1 != 0 || |
158 | dest.x2 != crtc->mode.hdisplay || dest.y2 != crtc->mode.vdisplay) { |
159 | DRM_DEBUG_KMS("Primary plane must cover entire CRTC\n" ); |
160 | return -EINVAL; |
161 | } |
162 | |
163 | /* Framebuffer must be big enough to cover entire plane */ |
164 | ret = drm_crtc_check_viewport(crtc, crtc_x, crtc_y, &crtc->mode, fb); |
165 | if (ret) |
166 | return ret; |
167 | |
168 | /* Find current connectors for CRTC */ |
169 | num_connectors = get_connectors_for_crtc(crtc, NULL, 0); |
170 | BUG_ON(num_connectors == 0); |
171 | connector_list = kzalloc(num_connectors * sizeof(*connector_list), |
172 | GFP_KERNEL); |
173 | if (!connector_list) |
174 | return -ENOMEM; |
175 | get_connectors_for_crtc(crtc, connector_list, num_connectors); |
176 | |
177 | set.connectors = connector_list; |
178 | set.num_connectors = num_connectors; |
179 | |
180 | /* |
181 | * set_config() adjusts crtc->primary->fb; however the DRM setplane |
182 | * code that called us expects to handle the framebuffer update and |
183 | * reference counting; save and restore the current fb before |
184 | * calling it. |
185 | * |
186 | * N.B., we call set_config() directly here rather than using |
187 | * drm_mode_set_config_internal. We're reprogramming the same |
188 | * connectors that were already in use, so we shouldn't need the extra |
189 | * cross-CRTC fb refcounting to accomodate stealing connectors. |
190 | * drm_mode_setplane() already handles the basic refcounting for the |
191 | * framebuffers involved in this operation. |
192 | */ |
193 | tmpfb = plane->fb; |
194 | ret = crtc->funcs->set_config(&set); |
195 | plane->fb = tmpfb; |
196 | |
197 | kfree(connector_list); |
198 | return ret; |
199 | } |
200 | EXPORT_SYMBOL(drm_primary_helper_update); |
201 | |
202 | /** |
203 | * drm_primary_helper_disable() - Helper for primary plane disable |
204 | * @plane: plane to disable |
205 | * |
206 | * Provides a default plane disable handler for primary planes. This is handler |
207 | * is called in response to a userspace SetPlane operation on the plane with a |
208 | * NULL framebuffer parameter. It unconditionally fails the disable call with |
209 | * -EINVAL the only way to disable the primary plane without driver support is |
210 | * to disable the entier CRTC. Which does not match the plane ->disable hook. |
211 | * |
212 | * Note that some hardware may be able to disable the primary plane without |
213 | * disabling the whole CRTC. Drivers for such hardware should provide their |
214 | * own disable handler that disables just the primary plane (and they'll likely |
215 | * need to provide their own update handler as well to properly re-enable a |
216 | * disabled primary plane). |
217 | * |
218 | * RETURNS: |
219 | * Unconditionally returns -EINVAL. |
220 | */ |
221 | int drm_primary_helper_disable(struct drm_plane *plane) |
222 | { |
223 | return -EINVAL; |
224 | } |
225 | EXPORT_SYMBOL(drm_primary_helper_disable); |
226 | |
227 | /** |
228 | * drm_primary_helper_destroy() - Helper for primary plane destruction |
229 | * @plane: plane to destroy |
230 | * |
231 | * Provides a default plane destroy handler for primary planes. This handler |
232 | * is called during CRTC destruction. We disable the primary plane, remove |
233 | * it from the DRM plane list, and deallocate the plane structure. |
234 | */ |
235 | void drm_primary_helper_destroy(struct drm_plane *plane) |
236 | { |
237 | plane->funcs->disable_plane(plane); |
238 | drm_plane_cleanup(plane); |
239 | kfree(plane); |
240 | } |
241 | EXPORT_SYMBOL(drm_primary_helper_destroy); |
242 | |
243 | const struct drm_plane_funcs drm_primary_helper_funcs = { |
244 | .update_plane = drm_primary_helper_update, |
245 | .disable_plane = drm_primary_helper_disable, |
246 | .destroy = drm_primary_helper_destroy, |
247 | }; |
248 | EXPORT_SYMBOL(drm_primary_helper_funcs); |
249 | |
250 | /** |
251 | * drm_primary_helper_create_plane() - Create a generic primary plane |
252 | * @dev: drm device |
253 | * @formats: pixel formats supported, or NULL for a default safe list |
254 | * @num_formats: size of @formats; ignored if @formats is NULL |
255 | * |
256 | * Allocates and initializes a primary plane that can be used with the primary |
257 | * plane helpers. Drivers that wish to use driver-specific plane structures or |
258 | * provide custom handler functions may perform their own allocation and |
259 | * initialization rather than calling this function. |
260 | */ |
261 | struct drm_plane *drm_primary_helper_create_plane(struct drm_device *dev, |
262 | const uint32_t *formats, |
263 | int num_formats) |
264 | { |
265 | struct drm_plane *primary; |
266 | int ret; |
267 | |
268 | primary = kzalloc(sizeof(*primary), GFP_KERNEL); |
269 | if (primary == NULL) { |
270 | DRM_DEBUG_KMS("Failed to allocate primary plane\n" ); |
271 | return NULL; |
272 | } |
273 | |
274 | if (formats == NULL) { |
275 | formats = safe_modeset_formats; |
276 | num_formats = ARRAY_SIZE(safe_modeset_formats); |
277 | } |
278 | |
279 | /* possible_crtc's will be filled in later by crtc_init */ |
280 | ret = drm_plane_init(dev, primary, 0, &drm_primary_helper_funcs, |
281 | formats, num_formats, |
282 | DRM_PLANE_TYPE_PRIMARY); |
283 | if (ret) { |
284 | kfree(primary); |
285 | primary = NULL; |
286 | } |
287 | |
288 | return primary; |
289 | } |
290 | EXPORT_SYMBOL(drm_primary_helper_create_plane); |
291 | |
292 | /** |
293 | * drm_crtc_init - Legacy CRTC initialization function |
294 | * @dev: DRM device |
295 | * @crtc: CRTC object to init |
296 | * @funcs: callbacks for the new CRTC |
297 | * |
298 | * Initialize a CRTC object with a default helper-provided primary plane and no |
299 | * cursor plane. |
300 | * |
301 | * Returns: |
302 | * Zero on success, error code on failure. |
303 | */ |
304 | int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, |
305 | const struct drm_crtc_funcs *funcs) |
306 | { |
307 | struct drm_plane *primary; |
308 | |
309 | primary = drm_primary_helper_create_plane(dev, NULL, 0); |
310 | return drm_crtc_init_with_planes(dev, crtc, primary, NULL, funcs); |
311 | } |
312 | EXPORT_SYMBOL(drm_crtc_init); |
313 | |