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