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