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> | |
0500c04e | 27 | |
321ebf04 | 28 | #include <drm/drm_atomic.h> |
0500c04e | 29 | #include <drm/drm_atomic_helper.h> |
72fdb40c | 30 | #include <drm/drm_atomic_uapi.h> |
0500c04e | 31 | #include <drm/drm_device.h> |
fa0706e9 | 32 | #include <drm/drm_drv.h> |
9338203c | 33 | #include <drm/drm_encoder.h> |
0500c04e | 34 | #include <drm/drm_plane_helper.h> |
fa0706e9 | 35 | #include <drm/drm_print.h> |
0500c04e | 36 | #include <drm/drm_rect.h> |
c103d1cf MR |
37 | |
38 | #define SUBPIXEL_MASK 0xffff | |
39 | ||
3150c7d0 DV |
40 | /** |
41 | * DOC: overview | |
42 | * | |
81ed7d73 GU |
43 | * This helper library contains helpers to implement primary plane support on |
44 | * top of the normal CRTC configuration interface. | |
6806cdf9 DV |
45 | * Since the legacy &drm_mode_config_funcs.set_config interface ties the primary |
46 | * plane together with the CRTC state this does not allow userspace to disable | |
6b6fce62 DV |
47 | * the primary plane itself. The default primary plane only expose XRBG8888 and |
48 | * ARGB8888 as valid pixel formats for the attached framebuffer. | |
3150c7d0 DV |
49 | * |
50 | * Drivers are highly recommended to implement proper support for primary | |
51 | * planes, and newly merged drivers must not rely upon these transitional | |
52 | * helpers. | |
53 | * | |
092d01da | 54 | * The plane helpers share the function table structures with other helpers, |
ea0dd85a | 55 | * specifically also the atomic helpers. See &struct drm_plane_helper_funcs for |
092d01da | 56 | * the details. |
3150c7d0 DV |
57 | */ |
58 | ||
c103d1cf MR |
59 | /* |
60 | * Returns the connectors currently associated with a CRTC. This function | |
61 | * should be called twice: once with a NULL connector list to retrieve | |
62 | * the list size, and once with the properly allocated list to be filled in. | |
63 | */ | |
64 | static int get_connectors_for_crtc(struct drm_crtc *crtc, | |
65 | struct drm_connector **connector_list, | |
66 | int num_connectors) | |
67 | { | |
68 | struct drm_device *dev = crtc->dev; | |
69 | struct drm_connector *connector; | |
c36a3254 | 70 | struct drm_connector_list_iter conn_iter; |
c103d1cf MR |
71 | int count = 0; |
72 | ||
6e9f798d DV |
73 | /* |
74 | * Note: Once we change the plane hooks to more fine-grained locking we | |
75 | * need to grab the connection_mutex here to be able to make these | |
76 | * checks. | |
77 | */ | |
51fd371b | 78 | WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex)); |
6e9f798d | 79 | |
b982dab1 | 80 | drm_connector_list_iter_begin(dev, &conn_iter); |
c36a3254 | 81 | drm_for_each_connector_iter(connector, &conn_iter) { |
c103d1cf MR |
82 | if (connector->encoder && connector->encoder->crtc == crtc) { |
83 | if (connector_list != NULL && count < num_connectors) | |
84 | *(connector_list++) = connector; | |
85 | ||
86 | count++; | |
87 | } | |
9a9f5ce8 | 88 | } |
b982dab1 | 89 | drm_connector_list_iter_end(&conn_iter); |
c103d1cf MR |
90 | |
91 | return count; | |
92 | } | |
93 | ||
84c08517 DV |
94 | static int drm_plane_helper_check_update(struct drm_plane *plane, |
95 | struct drm_crtc *crtc, | |
96 | struct drm_framebuffer *fb, | |
97 | struct drm_rect *src, | |
98 | struct drm_rect *dst, | |
99 | unsigned int rotation, | |
100 | int min_scale, | |
101 | int max_scale, | |
102 | bool can_position, | |
103 | bool can_update_disabled, | |
104 | bool *visible) | |
df86af91 | 105 | { |
10b47ee0 | 106 | struct drm_plane_state plane_state = { |
df86af91 VS |
107 | .plane = plane, |
108 | .crtc = crtc, | |
109 | .fb = fb, | |
110 | .src_x = src->x1, | |
111 | .src_y = src->y1, | |
112 | .src_w = drm_rect_width(src), | |
113 | .src_h = drm_rect_height(src), | |
114 | .crtc_x = dst->x1, | |
115 | .crtc_y = dst->y1, | |
116 | .crtc_w = drm_rect_width(dst), | |
117 | .crtc_h = drm_rect_height(dst), | |
118 | .rotation = rotation, | |
df86af91 | 119 | }; |
10b47ee0 VS |
120 | struct drm_crtc_state crtc_state = { |
121 | .crtc = crtc, | |
122 | .enable = crtc->enabled, | |
81af63a4 | 123 | .mode = crtc->mode, |
10b47ee0 | 124 | }; |
df86af91 VS |
125 | int ret; |
126 | ||
a01cb8ba | 127 | ret = drm_atomic_helper_check_plane_state(&plane_state, &crtc_state, |
81af63a4 | 128 | min_scale, max_scale, |
a01cb8ba VS |
129 | can_position, |
130 | can_update_disabled); | |
df86af91 VS |
131 | if (ret) |
132 | return ret; | |
133 | ||
10b47ee0 VS |
134 | *src = plane_state.src; |
135 | *dst = plane_state.dst; | |
136 | *visible = plane_state.visible; | |
df86af91 VS |
137 | |
138 | return 0; | |
139 | } | |
7daf8d54 | 140 | |
30c63715 TZ |
141 | /** |
142 | * drm_plane_helper_update_primary - Helper for updating primary planes | |
143 | * @plane: plane to update | |
144 | * @crtc: the plane's new CRTC | |
145 | * @fb: the plane's new framebuffer | |
146 | * @crtc_x: x coordinate within CRTC | |
147 | * @crtc_y: y coordinate within CRTC | |
148 | * @crtc_w: width coordinate within CRTC | |
149 | * @crtc_h: height coordinate within CRTC | |
150 | * @src_x: x coordinate within source | |
151 | * @src_y: y coordinate within source | |
152 | * @src_w: width coordinate within source | |
153 | * @src_h: height coordinate within source | |
154 | * @ctx: modeset locking context | |
155 | * | |
156 | * This helper validates the given parameters and updates the primary plane. | |
157 | * | |
158 | * This function is only useful for non-atomic modesetting. Don't use | |
159 | * it in new drivers. | |
160 | * | |
161 | * Returns: | |
162 | * Zero on success, or an errno code otherwise. | |
163 | */ | |
164 | int drm_plane_helper_update_primary(struct drm_plane *plane, struct drm_crtc *crtc, | |
165 | struct drm_framebuffer *fb, | |
166 | int crtc_x, int crtc_y, | |
167 | unsigned int crtc_w, unsigned int crtc_h, | |
168 | uint32_t src_x, uint32_t src_y, | |
169 | uint32_t src_w, uint32_t src_h, | |
170 | struct drm_modeset_acquire_ctx *ctx) | |
c103d1cf MR |
171 | { |
172 | struct drm_mode_set set = { | |
173 | .crtc = crtc, | |
174 | .fb = fb, | |
175 | .mode = &crtc->mode, | |
176 | .x = src_x >> 16, | |
177 | .y = src_y >> 16, | |
178 | }; | |
7daf8d54 MR |
179 | struct drm_rect src = { |
180 | .x1 = src_x, | |
181 | .y1 = src_y, | |
182 | .x2 = src_x + src_w, | |
183 | .y2 = src_y + src_h, | |
184 | }; | |
c103d1cf MR |
185 | struct drm_rect dest = { |
186 | .x1 = crtc_x, | |
187 | .y1 = crtc_y, | |
188 | .x2 = crtc_x + crtc_w, | |
189 | .y2 = crtc_y + crtc_h, | |
190 | }; | |
fa0706e9 | 191 | struct drm_device *dev = plane->dev; |
c103d1cf | 192 | struct drm_connector **connector_list; |
c103d1cf | 193 | int num_connectors, ret; |
7daf8d54 | 194 | bool visible; |
c103d1cf | 195 | |
fa0706e9 TZ |
196 | if (drm_WARN_ON_ONCE(dev, drm_drv_uses_atomic_modeset(dev))) |
197 | return -EINVAL; | |
198 | ||
7daf8d54 | 199 | ret = drm_plane_helper_check_update(plane, crtc, fb, |
81af63a4 | 200 | &src, &dest, |
c2c446ad | 201 | DRM_MODE_ROTATE_0, |
cce32e4e TZ |
202 | DRM_PLANE_NO_SCALING, |
203 | DRM_PLANE_NO_SCALING, | |
7daf8d54 | 204 | false, false, &visible); |
c103d1cf MR |
205 | if (ret) |
206 | return ret; | |
207 | ||
7daf8d54 MR |
208 | if (!visible) |
209 | /* | |
210 | * Primary plane isn't visible. Note that unless a driver | |
211 | * provides their own disable function, this will just | |
212 | * wind up returning -EINVAL to userspace. | |
213 | */ | |
19315294 | 214 | return plane->funcs->disable_plane(plane, ctx); |
7daf8d54 | 215 | |
c103d1cf MR |
216 | /* Find current connectors for CRTC */ |
217 | num_connectors = get_connectors_for_crtc(crtc, NULL, 0); | |
218 | BUG_ON(num_connectors == 0); | |
4b947b1c | 219 | connector_list = kcalloc(num_connectors, sizeof(*connector_list), |
c103d1cf MR |
220 | GFP_KERNEL); |
221 | if (!connector_list) | |
222 | return -ENOMEM; | |
223 | get_connectors_for_crtc(crtc, connector_list, num_connectors); | |
224 | ||
225 | set.connectors = connector_list; | |
226 | set.num_connectors = num_connectors; | |
227 | ||
228 | /* | |
0fe27f06 | 229 | * We call set_config() directly here rather than using |
c103d1cf MR |
230 | * drm_mode_set_config_internal. We're reprogramming the same |
231 | * connectors that were already in use, so we shouldn't need the extra | |
0ae865ef | 232 | * cross-CRTC fb refcounting to accommodate stealing connectors. |
c103d1cf MR |
233 | * drm_mode_setplane() already handles the basic refcounting for the |
234 | * framebuffers involved in this operation. | |
235 | */ | |
a4eff9aa | 236 | ret = crtc->funcs->set_config(&set, ctx); |
c103d1cf MR |
237 | |
238 | kfree(connector_list); | |
239 | return ret; | |
240 | } | |
30c63715 | 241 | EXPORT_SYMBOL(drm_plane_helper_update_primary); |
c103d1cf | 242 | |
30c63715 TZ |
243 | /** |
244 | * drm_plane_helper_disable_primary - Helper for disabling primary planes | |
245 | * @plane: plane to disable | |
246 | * @ctx: modeset locking context | |
247 | * | |
248 | * This helper returns an error when trying to disable the primary | |
249 | * plane. | |
250 | * | |
251 | * This function is only useful for non-atomic modesetting. Don't use | |
252 | * it in new drivers. | |
253 | * | |
254 | * Returns: | |
255 | * An errno code. | |
256 | */ | |
257 | int drm_plane_helper_disable_primary(struct drm_plane *plane, | |
258 | struct drm_modeset_acquire_ctx *ctx) | |
c103d1cf | 259 | { |
fa0706e9 TZ |
260 | struct drm_device *dev = plane->dev; |
261 | ||
262 | drm_WARN_ON_ONCE(dev, drm_drv_uses_atomic_modeset(dev)); | |
263 | ||
b6ccd7b9 | 264 | return -EINVAL; |
c103d1cf | 265 | } |
30c63715 | 266 | EXPORT_SYMBOL(drm_plane_helper_disable_primary); |
c103d1cf MR |
267 | |
268 | /** | |
30c63715 | 269 | * drm_plane_helper_destroy() - Helper for primary plane destruction |
c103d1cf MR |
270 | * @plane: plane to destroy |
271 | * | |
272 | * Provides a default plane destroy handler for primary planes. This handler | |
273 | * is called during CRTC destruction. We disable the primary plane, remove | |
274 | * it from the DRM plane list, and deallocate the plane structure. | |
275 | */ | |
30c63715 | 276 | void drm_plane_helper_destroy(struct drm_plane *plane) |
c103d1cf | 277 | { |
c103d1cf MR |
278 | drm_plane_cleanup(plane); |
279 | kfree(plane); | |
280 | } | |
30c63715 | 281 | EXPORT_SYMBOL(drm_plane_helper_destroy); |