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