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 28 #include <drm/drm_atomic.h> 29 #include <drm/drm_atomic_helper.h> 30 #include <drm/drm_atomic_uapi.h> 31 #include <drm/drm_crtc_helper.h> 32 #include <drm/drm_device.h> 33 #include <drm/drm_encoder.h> 34 #include <drm/drm_plane_helper.h> 35 #include <drm/drm_rect.h> 36 37 #define SUBPIXEL_MASK 0xffff 38 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. 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 46 * the primary plane itself. The default primary plane only expose XRBG8888 and 47 * ARGB8888 as valid pixel formats for the attached framebuffer. 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. 60 * 61 * The plane helpers share the function table structures with other helpers, 62 * specifically also the atomic helpers. See &struct drm_plane_helper_funcs for 63 * the details. 64 */ 65 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; 77 struct drm_connector_list_iter conn_iter; 78 int count = 0; 79 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 */ 85 WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex)); 86 87 drm_connector_list_iter_begin(dev, &conn_iter); 88 drm_for_each_connector_iter(connector, &conn_iter) { 89 if (connector->encoder && connector->encoder->crtc == crtc) { 90 if (connector_list != NULL && count < num_connectors) 91 *(connector_list++) = connector; 92 93 count++; 94 } 95 } 96 drm_connector_list_iter_end(&conn_iter); 97 98 return count; 99 } 100 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) 112 { 113 struct drm_plane_state plane_state = { 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 }; 128 struct drm_crtc_state crtc_state = { 129 .crtc = crtc, 130 .enable = crtc->enabled, 131 .mode = crtc->mode, 132 }; 133 int ret; 134 135 ret = drm_atomic_helper_check_plane_state(&plane_state, &crtc_state, 136 min_scale, max_scale, 137 can_position, 138 can_update_disabled); 139 if (ret) 140 return ret; 141 142 *src = plane_state.src; 143 *dst = plane_state.dst; 144 *visible = plane_state.visible; 145 146 return 0; 147 } 148 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) 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 }; 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 }; 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 }; 176 struct drm_connector **connector_list; 177 int num_connectors, ret; 178 bool visible; 179 180 ret = drm_plane_helper_check_update(plane, crtc, fb, 181 &src, &dest, 182 DRM_MODE_ROTATE_0, 183 DRM_PLANE_HELPER_NO_SCALING, 184 DRM_PLANE_HELPER_NO_SCALING, 185 false, false, &visible); 186 if (ret) 187 return ret; 188 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 */ 195 return plane->funcs->disable_plane(plane, ctx); 196 197 /* Find current connectors for CRTC */ 198 num_connectors = get_connectors_for_crtc(crtc, NULL, 0); 199 BUG_ON(num_connectors == 0); 200 connector_list = kcalloc(num_connectors, sizeof(*connector_list), 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 /* 210 * We call set_config() directly here rather than using 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 */ 217 ret = crtc->funcs->set_config(&set, ctx); 218 219 kfree(connector_list); 220 return ret; 221 } 222 223 static int drm_primary_helper_disable(struct drm_plane *plane, 224 struct drm_modeset_acquire_ctx *ctx) 225 { 226 return -EINVAL; 227 } 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 { 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); 250