1/* $NetBSD: drm_plane_helper.c,v 1.6 2021/12/19 09:49:56 riastradh Exp $ */ 2 3/* 4 * Copyright (C) 2014 Intel Corporation 5 * 6 * DRM universal plane helper functions 7 * 8 * Permission is hereby granted, free of charge, to any person obtaining a 9 * copy of this software and associated documentation files (the "Software"), 10 * to deal in the Software without restriction, including without limitation 11 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 12 * and/or sell copies of the Software, and to permit persons to whom the 13 * Software is furnished to do so, subject to the following conditions: 14 * 15 * The above copyright notice and this permission notice (including the next 16 * paragraph) shall be included in all copies or substantial portions of the 17 * Software. 18 * 19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 22 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 * SOFTWARE. 26 */ 27 28#include <sys/cdefs.h> 29__KERNEL_RCSID(0, "$NetBSD: drm_plane_helper.c,v 1.6 2021/12/19 09:49:56 riastradh Exp $"); 30 31#include <linux/list.h> 32 33#include <drm/drm_atomic.h> 34#include <drm/drm_atomic_helper.h> 35#include <drm/drm_atomic_uapi.h> 36#include <drm/drm_crtc_helper.h> 37#include <drm/drm_device.h> 38#include <drm/drm_encoder.h> 39#include <drm/drm_plane_helper.h> 40#include <drm/drm_rect.h> 41 42/** 43 * DOC: overview 44 * 45 * This helper library has two parts. The first part has support to implement 46 * primary plane support on top of the normal CRTC configuration interface. 47 * Since the legacy &drm_mode_config_funcs.set_config interface ties the primary 48 * plane together with the CRTC state this does not allow userspace to disable 49 * the primary plane itself. The default primary plane only expose XRBG8888 and 50 * ARGB8888 as valid pixel formats for the attached framebuffer. 51 * 52 * Drivers are highly recommended to implement proper support for primary 53 * planes, and newly merged drivers must not rely upon these transitional 54 * helpers. 55 * 56 * The second part also implements transitional helpers which allow drivers to 57 * gradually switch to the atomic helper infrastructure for plane updates. Once 58 * that switch is complete drivers shouldn't use these any longer, instead using 59 * the proper legacy implementations for update and disable plane hooks provided 60 * by the atomic helpers. 61 * 62 * Again drivers are strongly urged to switch to the new interfaces. 63 * 64 * The plane helpers share the function table structures with other helpers, 65 * specifically also the atomic helpers. See &struct drm_plane_helper_funcs for 66 * the details. 67 */ 68 69/* 70 * Returns the connectors currently associated with a CRTC. This function 71 * should be called twice: once with a NULL connector list to retrieve 72 * the list size, and once with the properly allocated list to be filled in. 73 */ 74static int get_connectors_for_crtc(struct drm_crtc *crtc, 75 struct drm_connector **connector_list, 76 int num_connectors) 77{ 78 struct drm_device *dev = crtc->dev; 79 struct drm_connector *connector; 80 struct drm_connector_list_iter conn_iter; 81 int count = 0; 82 83 /* 84 * Note: Once we change the plane hooks to more fine-grained locking we 85 * need to grab the connection_mutex here to be able to make these 86 * checks. 87 */ 88 WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex)); 89 90 drm_connector_list_iter_begin(dev, &conn_iter); 91 drm_for_each_connector_iter(connector, &conn_iter) { 92 if (connector->encoder && connector->encoder->crtc == crtc) { 93 if (connector_list != NULL && count < num_connectors) 94 *(connector_list++) = connector; 95 96 count++; 97 } 98 } 99 drm_connector_list_iter_end(&conn_iter); 100 101 return count; 102} 103 104static int drm_plane_helper_check_update(struct drm_plane *plane, 105 struct drm_crtc *crtc, 106 struct drm_framebuffer *fb, 107 struct drm_rect *src, 108 struct drm_rect *dst, 109 unsigned int rotation, 110 int min_scale, 111 int max_scale, 112 bool can_position, 113 bool can_update_disabled, 114 bool *visible) 115{ 116 struct drm_plane_state plane_state = { 117 .plane = plane, 118 .crtc = crtc, 119 .fb = fb, 120 .src_x = src->x1, 121 .src_y = src->y1, 122 .src_w = drm_rect_width(src), 123 .src_h = drm_rect_height(src), 124 .crtc_x = dst->x1, 125 .crtc_y = dst->y1, 126 .crtc_w = drm_rect_width(dst), 127 .crtc_h = drm_rect_height(dst), 128 .rotation = rotation, 129 .visible = *visible, 130 }; 131 struct drm_crtc_state crtc_state = { 132 .crtc = crtc, 133 .enable = crtc->enabled, 134 .mode = crtc->mode, 135 }; 136 int ret; 137 138 ret = drm_atomic_helper_check_plane_state(&plane_state, &crtc_state, 139 min_scale, max_scale, 140 can_position, 141 can_update_disabled); 142 if (ret) 143 return ret; 144 145 *src = plane_state.src; 146 *dst = plane_state.dst; 147 *visible = plane_state.visible; 148 149 return 0; 150} 151 152static int drm_primary_helper_update(struct drm_plane *plane, struct drm_crtc *crtc, 153 struct drm_framebuffer *fb, 154 int crtc_x, int crtc_y, 155 unsigned int crtc_w, unsigned int crtc_h, 156 uint32_t src_x, uint32_t src_y, 157 uint32_t src_w, uint32_t src_h, 158 struct drm_modeset_acquire_ctx *ctx) 159{ 160 struct drm_mode_set set = { 161 .crtc = crtc, 162 .fb = fb, 163 .mode = &crtc->mode, 164 .x = src_x >> 16, 165 .y = src_y >> 16, 166 }; 167 struct drm_rect src = { 168 .x1 = src_x, 169 .y1 = src_y, 170 .x2 = src_x + src_w, 171 .y2 = src_y + src_h, 172 }; 173 struct drm_rect dest = { 174 .x1 = crtc_x, 175 .y1 = crtc_y, 176 .x2 = crtc_x + crtc_w, 177 .y2 = crtc_y + crtc_h, 178 }; 179 struct drm_connector **connector_list; 180 int num_connectors, ret; 181 bool visible = false; /* XXX wasn't initialized normally. Looks like bug. */ 182 183 ret = drm_plane_helper_check_update(plane, crtc, fb, 184 &src, &dest, 185 DRM_MODE_ROTATE_0, 186 DRM_PLANE_HELPER_NO_SCALING, 187 DRM_PLANE_HELPER_NO_SCALING, 188 false, false, &visible); 189 if (ret) 190 return ret; 191 192 if (!visible) 193 /* 194 * Primary plane isn't visible. Note that unless a driver 195 * provides their own disable function, this will just 196 * wind up returning -EINVAL to userspace. 197 */ 198 return plane->funcs->disable_plane(plane, ctx); 199 200 /* Find current connectors for CRTC */ 201 num_connectors = get_connectors_for_crtc(crtc, NULL, 0); 202 BUG_ON(num_connectors == 0); 203 connector_list = kcalloc(num_connectors, sizeof(*connector_list), 204 GFP_KERNEL); 205 if (!connector_list) 206 return -ENOMEM; 207 get_connectors_for_crtc(crtc, connector_list, num_connectors); 208 209 set.connectors = connector_list; 210 set.num_connectors = num_connectors; 211 212 /* 213 * We call set_config() directly here rather than using 214 * drm_mode_set_config_internal. We're reprogramming the same 215 * connectors that were already in use, so we shouldn't need the extra 216 * cross-CRTC fb refcounting to accomodate stealing connectors. 217 * drm_mode_setplane() already handles the basic refcounting for the 218 * framebuffers involved in this operation. 219 */ 220 ret = crtc->funcs->set_config(&set, ctx); 221 222 kfree(connector_list); 223 return ret; 224} 225 226static int drm_primary_helper_disable(struct drm_plane *plane, 227 struct drm_modeset_acquire_ctx *ctx) 228{ 229 return -EINVAL; 230} 231 232/** 233 * drm_primary_helper_destroy() - Helper for primary plane destruction 234 * @plane: plane to destroy 235 * 236 * Provides a default plane destroy handler for primary planes. This handler 237 * is called during CRTC destruction. We disable the primary plane, remove 238 * it from the DRM plane list, and deallocate the plane structure. 239 */ 240void drm_primary_helper_destroy(struct drm_plane *plane) 241{ 242 drm_plane_cleanup(plane); 243 kfree(plane); 244} 245EXPORT_SYMBOL(drm_primary_helper_destroy); 246 247const struct drm_plane_funcs drm_primary_helper_funcs = { 248 .update_plane = drm_primary_helper_update, 249 .disable_plane = drm_primary_helper_disable, 250 .destroy = drm_primary_helper_destroy, 251}; 252EXPORT_SYMBOL(drm_primary_helper_funcs); 253