1/* $NetBSD: drm_modeset_helper.c,v 1.2 2021/12/18 23:44:57 riastradh Exp $ */ 2 3/* 4 * Copyright (c) 2016 Intel Corporation 5 * 6 * Permission to use, copy, modify, distribute, and sell this software and its 7 * documentation for any purpose is hereby granted without fee, provided that 8 * the above copyright notice appear in all copies and that both that copyright 9 * notice and this permission notice appear in supporting documentation, and 10 * that the name of the copyright holders not be used in advertising or 11 * publicity pertaining to distribution of the software without specific, 12 * written prior permission. The copyright holders make no representations 13 * about the suitability of this software for any purpose. It is provided "as 14 * is" without express or implied warranty. 15 * 16 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 17 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 18 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR 19 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 20 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 21 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 22 * OF THIS SOFTWARE. 23 */ 24 25#include <sys/cdefs.h> 26__KERNEL_RCSID(0, "$NetBSD: drm_modeset_helper.c,v 1.2 2021/12/18 23:44:57 riastradh Exp $"); 27 28#include <drm/drm_atomic_helper.h> 29#include <drm/drm_fb_helper.h> 30#include <drm/drm_fourcc.h> 31#include <drm/drm_modeset_helper.h> 32#include <drm/drm_plane_helper.h> 33#include <drm/drm_print.h> 34#include <drm/drm_probe_helper.h> 35 36/** 37 * DOC: aux kms helpers 38 * 39 * This helper library contains various one-off functions which don't really fit 40 * anywhere else in the DRM modeset helper library. 41 */ 42 43/** 44 * drm_helper_move_panel_connectors_to_head() - move panels to the front in the 45 * connector list 46 * @dev: drm device to operate on 47 * 48 * Some userspace presumes that the first connected connector is the main 49 * display, where it's supposed to display e.g. the login screen. For 50 * laptops, this should be the main panel. Use this function to sort all 51 * (eDP/LVDS/DSI) panels to the front of the connector list, instead of 52 * painstakingly trying to initialize them in the right order. 53 */ 54void drm_helper_move_panel_connectors_to_head(struct drm_device *dev) 55{ 56 struct drm_connector *connector, *tmp; 57 struct list_head panel_list; 58 59 INIT_LIST_HEAD(&panel_list); 60 61 spin_lock_irq(&dev->mode_config.connector_list_lock); 62 list_for_each_entry_safe(connector, tmp, 63 &dev->mode_config.connector_list, head) { 64 if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS || 65 connector->connector_type == DRM_MODE_CONNECTOR_eDP || 66 connector->connector_type == DRM_MODE_CONNECTOR_DSI) 67 list_move_tail(&connector->head, &panel_list); 68 } 69 70 list_splice(&panel_list, &dev->mode_config.connector_list); 71 spin_unlock_irq(&dev->mode_config.connector_list_lock); 72} 73EXPORT_SYMBOL(drm_helper_move_panel_connectors_to_head); 74 75/** 76 * drm_helper_mode_fill_fb_struct - fill out framebuffer metadata 77 * @dev: DRM device 78 * @fb: drm_framebuffer object to fill out 79 * @mode_cmd: metadata from the userspace fb creation request 80 * 81 * This helper can be used in a drivers fb_create callback to pre-fill the fb's 82 * metadata fields. 83 */ 84void drm_helper_mode_fill_fb_struct(struct drm_device *dev, 85 struct drm_framebuffer *fb, 86 const struct drm_mode_fb_cmd2 *mode_cmd) 87{ 88 int i; 89 90 fb->dev = dev; 91 fb->format = drm_get_format_info(dev, mode_cmd); 92 fb->width = mode_cmd->width; 93 fb->height = mode_cmd->height; 94 for (i = 0; i < 4; i++) { 95 fb->pitches[i] = mode_cmd->pitches[i]; 96 fb->offsets[i] = mode_cmd->offsets[i]; 97 } 98 fb->modifier = mode_cmd->modifier[0]; 99 fb->flags = mode_cmd->flags; 100} 101EXPORT_SYMBOL(drm_helper_mode_fill_fb_struct); 102 103/* 104 * This is the minimal list of formats that seem to be safe for modeset use 105 * with all current DRM drivers. Most hardware can actually support more 106 * formats than this and drivers may specify a more accurate list when 107 * creating the primary plane. However drivers that still call 108 * drm_plane_init() will use this minimal format list as the default. 109 */ 110static const uint32_t safe_modeset_formats[] = { 111 DRM_FORMAT_XRGB8888, 112 DRM_FORMAT_ARGB8888, 113}; 114 115static struct drm_plane *create_primary_plane(struct drm_device *dev) 116{ 117 struct drm_plane *primary; 118 int ret; 119 120 primary = kzalloc(sizeof(*primary), GFP_KERNEL); 121 if (primary == NULL) { 122 DRM_DEBUG_KMS("Failed to allocate primary plane\n"); 123 return NULL; 124 } 125 126 /* 127 * Remove the format_default field from drm_plane when dropping 128 * this helper. 129 */ 130 primary->format_default = true; 131 132 /* possible_crtc's will be filled in later by crtc_init */ 133 ret = drm_universal_plane_init(dev, primary, 0, 134 &drm_primary_helper_funcs, 135 safe_modeset_formats, 136 ARRAY_SIZE(safe_modeset_formats), 137 NULL, 138 DRM_PLANE_TYPE_PRIMARY, NULL); 139 if (ret) { 140 kfree(primary); 141 primary = NULL; 142 } 143 144 return primary; 145} 146 147/** 148 * drm_crtc_init - Legacy CRTC initialization function 149 * @dev: DRM device 150 * @crtc: CRTC object to init 151 * @funcs: callbacks for the new CRTC 152 * 153 * Initialize a CRTC object with a default helper-provided primary plane and no 154 * cursor plane. 155 * 156 * Note that we make some assumptions about hardware limitations that may not be 157 * true for all hardware: 158 * 159 * 1. Primary plane cannot be repositioned. 160 * 2. Primary plane cannot be scaled. 161 * 3. Primary plane must cover the entire CRTC. 162 * 4. Subpixel positioning is not supported. 163 * 5. The primary plane must always be on if the CRTC is enabled. 164 * 165 * This is purely a backwards compatibility helper for old drivers. Drivers 166 * should instead implement their own primary plane. Atomic drivers must do so. 167 * Drivers with the above hardware restriction can look into using &struct 168 * drm_simple_display_pipe, which encapsulates the above limitations into a nice 169 * interface. 170 * 171 * Returns: 172 * Zero on success, error code on failure. 173 */ 174int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, 175 const struct drm_crtc_funcs *funcs) 176{ 177 struct drm_plane *primary; 178 179 primary = create_primary_plane(dev); 180 return drm_crtc_init_with_planes(dev, crtc, primary, NULL, funcs, 181 NULL); 182} 183EXPORT_SYMBOL(drm_crtc_init); 184 185/** 186 * drm_mode_config_helper_suspend - Modeset suspend helper 187 * @dev: DRM device 188 * 189 * This helper function takes care of suspending the modeset side. It disables 190 * output polling if initialized, suspends fbdev if used and finally calls 191 * drm_atomic_helper_suspend(). 192 * If suspending fails, fbdev and polling is re-enabled. 193 * 194 * Returns: 195 * Zero on success, negative error code on error. 196 * 197 * See also: 198 * drm_kms_helper_poll_disable() and drm_fb_helper_set_suspend_unlocked(). 199 */ 200int drm_mode_config_helper_suspend(struct drm_device *dev) 201{ 202 struct drm_atomic_state *state; 203 204 if (!dev) 205 return 0; 206 207 drm_kms_helper_poll_disable(dev); 208 drm_fb_helper_set_suspend_unlocked(dev->fb_helper, 1); 209 state = drm_atomic_helper_suspend(dev); 210 if (IS_ERR(state)) { 211 drm_fb_helper_set_suspend_unlocked(dev->fb_helper, 0); 212 drm_kms_helper_poll_enable(dev); 213 return PTR_ERR(state); 214 } 215 216 dev->mode_config.suspend_state = state; 217 218 return 0; 219} 220EXPORT_SYMBOL(drm_mode_config_helper_suspend); 221 222/** 223 * drm_mode_config_helper_resume - Modeset resume helper 224 * @dev: DRM device 225 * 226 * This helper function takes care of resuming the modeset side. It calls 227 * drm_atomic_helper_resume(), resumes fbdev if used and enables output polling 228 * if initiaized. 229 * 230 * Returns: 231 * Zero on success, negative error code on error. 232 * 233 * See also: 234 * drm_fb_helper_set_suspend_unlocked() and drm_kms_helper_poll_enable(). 235 */ 236int drm_mode_config_helper_resume(struct drm_device *dev) 237{ 238 int ret; 239 240 if (!dev) 241 return 0; 242 243 if (WARN_ON(!dev->mode_config.suspend_state)) 244 return -EINVAL; 245 246 ret = drm_atomic_helper_resume(dev, dev->mode_config.suspend_state); 247 if (ret) 248 DRM_ERROR("Failed to resume (%d)\n", ret); 249 dev->mode_config.suspend_state = NULL; 250 251 drm_fb_helper_set_suspend_unlocked(dev->fb_helper, 0); 252 drm_kms_helper_poll_enable(dev); 253 254 return ret; 255} 256EXPORT_SYMBOL(drm_mode_config_helper_resume); 257