1592ffb21SWarner Losh /* 2592ffb21SWarner Losh * Copyright (c) 2006-2008 Intel Corporation 3592ffb21SWarner Losh * Copyright (c) 2007 Dave Airlie <airlied@linux.ie> 4592ffb21SWarner Losh * Copyright (c) 2008 Red Hat Inc. 5592ffb21SWarner Losh * 6592ffb21SWarner Losh * DRM core CRTC related functions 7592ffb21SWarner Losh * 8592ffb21SWarner Losh * Permission to use, copy, modify, distribute, and sell this software and its 9592ffb21SWarner Losh * documentation for any purpose is hereby granted without fee, provided that 10592ffb21SWarner Losh * the above copyright notice appear in all copies and that both that copyright 11592ffb21SWarner Losh * notice and this permission notice appear in supporting documentation, and 12592ffb21SWarner Losh * that the name of the copyright holders not be used in advertising or 13592ffb21SWarner Losh * publicity pertaining to distribution of the software without specific, 14592ffb21SWarner Losh * written prior permission. The copyright holders make no representations 15592ffb21SWarner Losh * about the suitability of this software for any purpose. It is provided "as 16592ffb21SWarner Losh * is" without express or implied warranty. 17592ffb21SWarner Losh * 18592ffb21SWarner Losh * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 19592ffb21SWarner Losh * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 20592ffb21SWarner Losh * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR 21592ffb21SWarner Losh * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 22592ffb21SWarner Losh * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 23592ffb21SWarner Losh * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 24592ffb21SWarner Losh * OF THIS SOFTWARE. 25592ffb21SWarner Losh * 26592ffb21SWarner Losh * Authors: 27592ffb21SWarner Losh * Keith Packard 28592ffb21SWarner Losh * Eric Anholt <eric@anholt.net> 29592ffb21SWarner Losh * Dave Airlie <airlied@linux.ie> 30592ffb21SWarner Losh * Jesse Barnes <jesse.barnes@intel.com> 31592ffb21SWarner Losh */ 32592ffb21SWarner Losh #include <sys/cdefs.h> 33592ffb21SWarner Losh #include <dev/drm2/drmP.h> 34592ffb21SWarner Losh #include <dev/drm2/drm_crtc.h> 35592ffb21SWarner Losh #include <dev/drm2/drm_edid.h> 36592ffb21SWarner Losh #include <dev/drm2/drm_fourcc.h> 37592ffb21SWarner Losh 38592ffb21SWarner Losh static void drm_property_destroy_blob(struct drm_device *dev, 39592ffb21SWarner Losh struct drm_property_blob *blob); 40592ffb21SWarner Losh 41592ffb21SWarner Losh /* Avoid boilerplate. I'm tired of typing. */ 42592ffb21SWarner Losh #define DRM_ENUM_NAME_FN(fnname, list) \ 43592ffb21SWarner Losh char *fnname(int val) \ 44592ffb21SWarner Losh { \ 45592ffb21SWarner Losh int i; \ 46592ffb21SWarner Losh for (i = 0; i < ARRAY_SIZE(list); i++) { \ 47592ffb21SWarner Losh if (list[i].type == val) \ 48592ffb21SWarner Losh return list[i].name; \ 49592ffb21SWarner Losh } \ 50592ffb21SWarner Losh return "(unknown)"; \ 51592ffb21SWarner Losh } 52592ffb21SWarner Losh 53592ffb21SWarner Losh /* 54592ffb21SWarner Losh * Global properties 55592ffb21SWarner Losh */ 56592ffb21SWarner Losh static struct drm_prop_enum_list drm_dpms_enum_list[] = 57592ffb21SWarner Losh { { DRM_MODE_DPMS_ON, "On" }, 58592ffb21SWarner Losh { DRM_MODE_DPMS_STANDBY, "Standby" }, 59592ffb21SWarner Losh { DRM_MODE_DPMS_SUSPEND, "Suspend" }, 60592ffb21SWarner Losh { DRM_MODE_DPMS_OFF, "Off" } 61592ffb21SWarner Losh }; 62592ffb21SWarner Losh 63592ffb21SWarner Losh DRM_ENUM_NAME_FN(drm_get_dpms_name, drm_dpms_enum_list) 64592ffb21SWarner Losh 65592ffb21SWarner Losh /* 66592ffb21SWarner Losh * Optional properties 67592ffb21SWarner Losh */ 68592ffb21SWarner Losh static struct drm_prop_enum_list drm_scaling_mode_enum_list[] = 69592ffb21SWarner Losh { 70592ffb21SWarner Losh { DRM_MODE_SCALE_NONE, "None" }, 71592ffb21SWarner Losh { DRM_MODE_SCALE_FULLSCREEN, "Full" }, 72592ffb21SWarner Losh { DRM_MODE_SCALE_CENTER, "Center" }, 73592ffb21SWarner Losh { DRM_MODE_SCALE_ASPECT, "Full aspect" }, 74592ffb21SWarner Losh }; 75592ffb21SWarner Losh 76592ffb21SWarner Losh static struct drm_prop_enum_list drm_dithering_mode_enum_list[] = 77592ffb21SWarner Losh { 78592ffb21SWarner Losh { DRM_MODE_DITHERING_OFF, "Off" }, 79592ffb21SWarner Losh { DRM_MODE_DITHERING_ON, "On" }, 80592ffb21SWarner Losh { DRM_MODE_DITHERING_AUTO, "Automatic" }, 81592ffb21SWarner Losh }; 82592ffb21SWarner Losh 83592ffb21SWarner Losh /* 84592ffb21SWarner Losh * Non-global properties, but "required" for certain connectors. 85592ffb21SWarner Losh */ 86592ffb21SWarner Losh static struct drm_prop_enum_list drm_dvi_i_select_enum_list[] = 87592ffb21SWarner Losh { 88592ffb21SWarner Losh { DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */ 89592ffb21SWarner Losh { DRM_MODE_SUBCONNECTOR_DVID, "DVI-D" }, /* DVI-I */ 90592ffb21SWarner Losh { DRM_MODE_SUBCONNECTOR_DVIA, "DVI-A" }, /* DVI-I */ 91592ffb21SWarner Losh }; 92592ffb21SWarner Losh 93592ffb21SWarner Losh DRM_ENUM_NAME_FN(drm_get_dvi_i_select_name, drm_dvi_i_select_enum_list) 94592ffb21SWarner Losh 95592ffb21SWarner Losh static struct drm_prop_enum_list drm_dvi_i_subconnector_enum_list[] = 96592ffb21SWarner Losh { 97592ffb21SWarner Losh { DRM_MODE_SUBCONNECTOR_Unknown, "Unknown" }, /* DVI-I and TV-out */ 98592ffb21SWarner Losh { DRM_MODE_SUBCONNECTOR_DVID, "DVI-D" }, /* DVI-I */ 99592ffb21SWarner Losh { DRM_MODE_SUBCONNECTOR_DVIA, "DVI-A" }, /* DVI-I */ 100592ffb21SWarner Losh }; 101592ffb21SWarner Losh 102592ffb21SWarner Losh DRM_ENUM_NAME_FN(drm_get_dvi_i_subconnector_name, 103592ffb21SWarner Losh drm_dvi_i_subconnector_enum_list) 104592ffb21SWarner Losh 105592ffb21SWarner Losh static struct drm_prop_enum_list drm_tv_select_enum_list[] = 106592ffb21SWarner Losh { 107592ffb21SWarner Losh { DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */ 108592ffb21SWarner Losh { DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */ 109592ffb21SWarner Losh { DRM_MODE_SUBCONNECTOR_SVIDEO, "SVIDEO" }, /* TV-out */ 110592ffb21SWarner Losh { DRM_MODE_SUBCONNECTOR_Component, "Component" }, /* TV-out */ 111592ffb21SWarner Losh { DRM_MODE_SUBCONNECTOR_SCART, "SCART" }, /* TV-out */ 112592ffb21SWarner Losh }; 113592ffb21SWarner Losh 114592ffb21SWarner Losh DRM_ENUM_NAME_FN(drm_get_tv_select_name, drm_tv_select_enum_list) 115592ffb21SWarner Losh 116592ffb21SWarner Losh static struct drm_prop_enum_list drm_tv_subconnector_enum_list[] = 117592ffb21SWarner Losh { 118592ffb21SWarner Losh { DRM_MODE_SUBCONNECTOR_Unknown, "Unknown" }, /* DVI-I and TV-out */ 119592ffb21SWarner Losh { DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */ 120592ffb21SWarner Losh { DRM_MODE_SUBCONNECTOR_SVIDEO, "SVIDEO" }, /* TV-out */ 121592ffb21SWarner Losh { DRM_MODE_SUBCONNECTOR_Component, "Component" }, /* TV-out */ 122592ffb21SWarner Losh { DRM_MODE_SUBCONNECTOR_SCART, "SCART" }, /* TV-out */ 123592ffb21SWarner Losh }; 124592ffb21SWarner Losh 125592ffb21SWarner Losh DRM_ENUM_NAME_FN(drm_get_tv_subconnector_name, 126592ffb21SWarner Losh drm_tv_subconnector_enum_list) 127592ffb21SWarner Losh 128592ffb21SWarner Losh static struct drm_prop_enum_list drm_dirty_info_enum_list[] = { 129592ffb21SWarner Losh { DRM_MODE_DIRTY_OFF, "Off" }, 130592ffb21SWarner Losh { DRM_MODE_DIRTY_ON, "On" }, 131592ffb21SWarner Losh { DRM_MODE_DIRTY_ANNOTATE, "Annotate" }, 132592ffb21SWarner Losh }; 133592ffb21SWarner Losh 134592ffb21SWarner Losh DRM_ENUM_NAME_FN(drm_get_dirty_info_name, 135592ffb21SWarner Losh drm_dirty_info_enum_list) 136592ffb21SWarner Losh 137592ffb21SWarner Losh struct drm_conn_prop_enum_list { 138592ffb21SWarner Losh int type; 139592ffb21SWarner Losh char *name; 140592ffb21SWarner Losh int count; 141592ffb21SWarner Losh }; 142592ffb21SWarner Losh 143592ffb21SWarner Losh /* 144592ffb21SWarner Losh * Connector and encoder types. 145592ffb21SWarner Losh */ 146592ffb21SWarner Losh static struct drm_conn_prop_enum_list drm_connector_enum_list[] = 147592ffb21SWarner Losh { { DRM_MODE_CONNECTOR_Unknown, "Unknown", 0 }, 148592ffb21SWarner Losh { DRM_MODE_CONNECTOR_VGA, "VGA", 0 }, 149592ffb21SWarner Losh { DRM_MODE_CONNECTOR_DVII, "DVI-I", 0 }, 150592ffb21SWarner Losh { DRM_MODE_CONNECTOR_DVID, "DVI-D", 0 }, 151592ffb21SWarner Losh { DRM_MODE_CONNECTOR_DVIA, "DVI-A", 0 }, 152592ffb21SWarner Losh { DRM_MODE_CONNECTOR_Composite, "Composite", 0 }, 153592ffb21SWarner Losh { DRM_MODE_CONNECTOR_SVIDEO, "SVIDEO", 0 }, 154592ffb21SWarner Losh { DRM_MODE_CONNECTOR_LVDS, "LVDS", 0 }, 155592ffb21SWarner Losh { DRM_MODE_CONNECTOR_Component, "Component", 0 }, 156592ffb21SWarner Losh { DRM_MODE_CONNECTOR_9PinDIN, "DIN", 0 }, 157592ffb21SWarner Losh { DRM_MODE_CONNECTOR_DisplayPort, "DP", 0 }, 158592ffb21SWarner Losh { DRM_MODE_CONNECTOR_HDMIA, "HDMI-A", 0 }, 159592ffb21SWarner Losh { DRM_MODE_CONNECTOR_HDMIB, "HDMI-B", 0 }, 160592ffb21SWarner Losh { DRM_MODE_CONNECTOR_TV, "TV", 0 }, 161592ffb21SWarner Losh { DRM_MODE_CONNECTOR_eDP, "eDP", 0 }, 162592ffb21SWarner Losh { DRM_MODE_CONNECTOR_VIRTUAL, "Virtual", 0}, 163592ffb21SWarner Losh }; 164592ffb21SWarner Losh 165592ffb21SWarner Losh static struct drm_prop_enum_list drm_encoder_enum_list[] = 166592ffb21SWarner Losh { { DRM_MODE_ENCODER_NONE, "None" }, 167592ffb21SWarner Losh { DRM_MODE_ENCODER_DAC, "DAC" }, 168592ffb21SWarner Losh { DRM_MODE_ENCODER_TMDS, "TMDS" }, 169592ffb21SWarner Losh { DRM_MODE_ENCODER_LVDS, "LVDS" }, 170592ffb21SWarner Losh { DRM_MODE_ENCODER_TVDAC, "TV" }, 171592ffb21SWarner Losh { DRM_MODE_ENCODER_VIRTUAL, "Virtual" }, 172592ffb21SWarner Losh }; 173592ffb21SWarner Losh 174592ffb21SWarner Losh char *drm_get_encoder_name(struct drm_encoder *encoder) 175592ffb21SWarner Losh { 176592ffb21SWarner Losh static char buf[32]; 177592ffb21SWarner Losh 178592ffb21SWarner Losh snprintf(buf, 32, "%s-%d", 179592ffb21SWarner Losh drm_encoder_enum_list[encoder->encoder_type].name, 180592ffb21SWarner Losh encoder->base.id); 181592ffb21SWarner Losh return buf; 182592ffb21SWarner Losh } 183592ffb21SWarner Losh EXPORT_SYMBOL(drm_get_encoder_name); 184592ffb21SWarner Losh 185592ffb21SWarner Losh char *drm_get_connector_name(struct drm_connector *connector) 186592ffb21SWarner Losh { 187592ffb21SWarner Losh static char buf[32]; 188592ffb21SWarner Losh 189592ffb21SWarner Losh snprintf(buf, 32, "%s-%d", 190592ffb21SWarner Losh drm_connector_enum_list[connector->connector_type].name, 191592ffb21SWarner Losh connector->connector_type_id); 192592ffb21SWarner Losh return buf; 193592ffb21SWarner Losh } 194592ffb21SWarner Losh EXPORT_SYMBOL(drm_get_connector_name); 195592ffb21SWarner Losh 196592ffb21SWarner Losh char *drm_get_connector_status_name(enum drm_connector_status status) 197592ffb21SWarner Losh { 198592ffb21SWarner Losh if (status == connector_status_connected) 199592ffb21SWarner Losh return "connected"; 200592ffb21SWarner Losh else if (status == connector_status_disconnected) 201592ffb21SWarner Losh return "disconnected"; 202592ffb21SWarner Losh else 203592ffb21SWarner Losh return "unknown"; 204592ffb21SWarner Losh } 205592ffb21SWarner Losh 206592ffb21SWarner Losh /** 207592ffb21SWarner Losh * drm_mode_object_get - allocate a new identifier 208592ffb21SWarner Losh * @dev: DRM device 209592ffb21SWarner Losh * @ptr: object pointer, used to generate unique ID 210592ffb21SWarner Losh * @type: object type 211592ffb21SWarner Losh * 212592ffb21SWarner Losh * LOCKING: 213592ffb21SWarner Losh * 214592ffb21SWarner Losh * Create a unique identifier based on @ptr in @dev's identifier space. Used 215592ffb21SWarner Losh * for tracking modes, CRTCs and connectors. 216592ffb21SWarner Losh * 217592ffb21SWarner Losh * RETURNS: 218592ffb21SWarner Losh * New unique (relative to other objects in @dev) integer identifier for the 219592ffb21SWarner Losh * object. 220592ffb21SWarner Losh */ 221592ffb21SWarner Losh static int drm_mode_object_get(struct drm_device *dev, 222592ffb21SWarner Losh struct drm_mode_object *obj, uint32_t obj_type) 223592ffb21SWarner Losh { 224592ffb21SWarner Losh int new_id = 0; 225592ffb21SWarner Losh int ret; 226592ffb21SWarner Losh 227592ffb21SWarner Losh ret = drm_gem_name_create(&dev->mode_config.crtc_names, obj, &new_id); 228592ffb21SWarner Losh if (ret) 229592ffb21SWarner Losh return ret; 230592ffb21SWarner Losh 231592ffb21SWarner Losh obj->id = new_id; 232592ffb21SWarner Losh obj->type = obj_type; 233592ffb21SWarner Losh return 0; 234592ffb21SWarner Losh } 235592ffb21SWarner Losh 236592ffb21SWarner Losh /** 237592ffb21SWarner Losh * drm_mode_object_put - free an identifer 238592ffb21SWarner Losh * @dev: DRM device 239592ffb21SWarner Losh * @id: ID to free 240592ffb21SWarner Losh * 241592ffb21SWarner Losh * LOCKING: 242592ffb21SWarner Losh * Caller must hold DRM mode_config lock. 243592ffb21SWarner Losh * 244592ffb21SWarner Losh * Free @id from @dev's unique identifier pool. 245592ffb21SWarner Losh */ 246592ffb21SWarner Losh static void drm_mode_object_put(struct drm_device *dev, 247592ffb21SWarner Losh struct drm_mode_object *object) 248592ffb21SWarner Losh { 249592ffb21SWarner Losh 250592ffb21SWarner Losh drm_gem_names_remove(&dev->mode_config.crtc_names, object->id); 251592ffb21SWarner Losh } 252592ffb21SWarner Losh 253592ffb21SWarner Losh struct drm_mode_object *drm_mode_object_find(struct drm_device *dev, 254592ffb21SWarner Losh uint32_t id, uint32_t type) 255592ffb21SWarner Losh { 256592ffb21SWarner Losh struct drm_mode_object *obj = NULL; 257592ffb21SWarner Losh 258592ffb21SWarner Losh obj = drm_gem_name_ref(&dev->mode_config.crtc_names, id, NULL); 259592ffb21SWarner Losh if (!obj || (obj->type != type) || (obj->id != id)) 260592ffb21SWarner Losh obj = NULL; 261592ffb21SWarner Losh 262592ffb21SWarner Losh return obj; 263592ffb21SWarner Losh } 264592ffb21SWarner Losh EXPORT_SYMBOL(drm_mode_object_find); 265592ffb21SWarner Losh 266592ffb21SWarner Losh /** 267592ffb21SWarner Losh * drm_framebuffer_init - initialize a framebuffer 268592ffb21SWarner Losh * @dev: DRM device 269592ffb21SWarner Losh * 270592ffb21SWarner Losh * LOCKING: 271592ffb21SWarner Losh * Caller must hold mode config lock. 272592ffb21SWarner Losh * 273592ffb21SWarner Losh * Allocates an ID for the framebuffer's parent mode object, sets its mode 274592ffb21SWarner Losh * functions & device file and adds it to the master fd list. 275592ffb21SWarner Losh * 276592ffb21SWarner Losh * RETURNS: 277592ffb21SWarner Losh * Zero on success, error code on failure. 278592ffb21SWarner Losh */ 279592ffb21SWarner Losh int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb, 280592ffb21SWarner Losh const struct drm_framebuffer_funcs *funcs) 281592ffb21SWarner Losh { 282592ffb21SWarner Losh int ret; 283592ffb21SWarner Losh 284592ffb21SWarner Losh refcount_init(&fb->refcount, 1); 285592ffb21SWarner Losh 286592ffb21SWarner Losh ret = drm_mode_object_get(dev, &fb->base, DRM_MODE_OBJECT_FB); 287592ffb21SWarner Losh if (ret) 288592ffb21SWarner Losh return ret; 289592ffb21SWarner Losh 290592ffb21SWarner Losh fb->dev = dev; 291592ffb21SWarner Losh fb->funcs = funcs; 292592ffb21SWarner Losh dev->mode_config.num_fb++; 293592ffb21SWarner Losh list_add(&fb->head, &dev->mode_config.fb_list); 294592ffb21SWarner Losh 295592ffb21SWarner Losh return 0; 296592ffb21SWarner Losh } 297592ffb21SWarner Losh EXPORT_SYMBOL(drm_framebuffer_init); 298592ffb21SWarner Losh 299592ffb21SWarner Losh static void drm_framebuffer_free(struct drm_framebuffer *fb) 300592ffb21SWarner Losh { 301592ffb21SWarner Losh fb->funcs->destroy(fb); 302592ffb21SWarner Losh } 303592ffb21SWarner Losh 304592ffb21SWarner Losh /** 305592ffb21SWarner Losh * drm_framebuffer_unreference - unref a framebuffer 306592ffb21SWarner Losh * 307592ffb21SWarner Losh * LOCKING: 308592ffb21SWarner Losh * Caller must hold mode config lock. 309592ffb21SWarner Losh */ 310592ffb21SWarner Losh void drm_framebuffer_unreference(struct drm_framebuffer *fb) 311592ffb21SWarner Losh { 312592ffb21SWarner Losh struct drm_device *dev = fb->dev; 313592ffb21SWarner Losh DRM_DEBUG("FB ID: %d\n", fb->base.id); 314592ffb21SWarner Losh if (!sx_xlocked(&dev->mode_config.mutex)) 315592ffb21SWarner Losh DRM_WARNING("%s: dev->mode_config.mutex not locked\n", __func__); 316592ffb21SWarner Losh if (refcount_release(&fb->refcount)) 317592ffb21SWarner Losh drm_framebuffer_free(fb); 318592ffb21SWarner Losh } 319592ffb21SWarner Losh EXPORT_SYMBOL(drm_framebuffer_unreference); 320592ffb21SWarner Losh 321592ffb21SWarner Losh /** 322592ffb21SWarner Losh * drm_framebuffer_reference - incr the fb refcnt 323592ffb21SWarner Losh */ 324592ffb21SWarner Losh void drm_framebuffer_reference(struct drm_framebuffer *fb) 325592ffb21SWarner Losh { 326592ffb21SWarner Losh DRM_DEBUG("FB ID: %d\n", fb->base.id); 327592ffb21SWarner Losh refcount_acquire(&fb->refcount); 328592ffb21SWarner Losh } 329592ffb21SWarner Losh EXPORT_SYMBOL(drm_framebuffer_reference); 330592ffb21SWarner Losh 331592ffb21SWarner Losh /** 332592ffb21SWarner Losh * drm_framebuffer_cleanup - remove a framebuffer object 333592ffb21SWarner Losh * @fb: framebuffer to remove 334592ffb21SWarner Losh * 335592ffb21SWarner Losh * LOCKING: 336592ffb21SWarner Losh * Caller must hold mode config lock. 337592ffb21SWarner Losh * 338592ffb21SWarner Losh * Scans all the CRTCs in @dev's mode_config. If they're using @fb, removes 339592ffb21SWarner Losh * it, setting it to NULL. 340592ffb21SWarner Losh */ 341592ffb21SWarner Losh void drm_framebuffer_cleanup(struct drm_framebuffer *fb) 342592ffb21SWarner Losh { 343592ffb21SWarner Losh struct drm_device *dev = fb->dev; 344592ffb21SWarner Losh /* 345592ffb21SWarner Losh * This could be moved to drm_framebuffer_remove(), but for 346592ffb21SWarner Losh * debugging is nice to keep around the list of fb's that are 347592ffb21SWarner Losh * no longer associated w/ a drm_file but are not unreferenced 348592ffb21SWarner Losh * yet. (i915 and omapdrm have debugfs files which will show 349592ffb21SWarner Losh * this.) 350592ffb21SWarner Losh */ 351592ffb21SWarner Losh drm_mode_object_put(dev, &fb->base); 352592ffb21SWarner Losh list_del(&fb->head); 353592ffb21SWarner Losh dev->mode_config.num_fb--; 354592ffb21SWarner Losh } 355592ffb21SWarner Losh EXPORT_SYMBOL(drm_framebuffer_cleanup); 356592ffb21SWarner Losh 357592ffb21SWarner Losh /** 358592ffb21SWarner Losh * drm_framebuffer_remove - remove and unreference a framebuffer object 359592ffb21SWarner Losh * @fb: framebuffer to remove 360592ffb21SWarner Losh * 361592ffb21SWarner Losh * LOCKING: 362592ffb21SWarner Losh * Caller must hold mode config lock. 363592ffb21SWarner Losh * 364592ffb21SWarner Losh * Scans all the CRTCs and planes in @dev's mode_config. If they're 365592ffb21SWarner Losh * using @fb, removes it, setting it to NULL. 366592ffb21SWarner Losh */ 367592ffb21SWarner Losh void drm_framebuffer_remove(struct drm_framebuffer *fb) 368592ffb21SWarner Losh { 369592ffb21SWarner Losh struct drm_device *dev = fb->dev; 370592ffb21SWarner Losh struct drm_crtc *crtc; 371592ffb21SWarner Losh struct drm_plane *plane; 372592ffb21SWarner Losh struct drm_mode_set set; 373592ffb21SWarner Losh int ret; 374592ffb21SWarner Losh 375592ffb21SWarner Losh /* remove from any CRTC */ 376592ffb21SWarner Losh list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { 377592ffb21SWarner Losh if (crtc->fb == fb) { 378592ffb21SWarner Losh /* should turn off the crtc */ 379592ffb21SWarner Losh memset(&set, 0, sizeof(struct drm_mode_set)); 380592ffb21SWarner Losh set.crtc = crtc; 381592ffb21SWarner Losh set.fb = NULL; 382592ffb21SWarner Losh ret = crtc->funcs->set_config(&set); 383592ffb21SWarner Losh if (ret) 384592ffb21SWarner Losh DRM_ERROR("failed to reset crtc %p when fb was deleted\n", crtc); 385592ffb21SWarner Losh } 386592ffb21SWarner Losh } 387592ffb21SWarner Losh 388592ffb21SWarner Losh list_for_each_entry(plane, &dev->mode_config.plane_list, head) { 389592ffb21SWarner Losh if (plane->fb == fb) { 390592ffb21SWarner Losh /* should turn off the crtc */ 391592ffb21SWarner Losh ret = plane->funcs->disable_plane(plane); 392592ffb21SWarner Losh if (ret) 393592ffb21SWarner Losh DRM_ERROR("failed to disable plane with busy fb\n"); 394592ffb21SWarner Losh /* disconnect the plane from the fb and crtc: */ 395592ffb21SWarner Losh plane->fb = NULL; 396592ffb21SWarner Losh plane->crtc = NULL; 397592ffb21SWarner Losh } 398592ffb21SWarner Losh } 399592ffb21SWarner Losh 400592ffb21SWarner Losh list_del(&fb->filp_head); 401592ffb21SWarner Losh 402592ffb21SWarner Losh drm_framebuffer_unreference(fb); 403592ffb21SWarner Losh } 404592ffb21SWarner Losh EXPORT_SYMBOL(drm_framebuffer_remove); 405592ffb21SWarner Losh 406592ffb21SWarner Losh /** 407592ffb21SWarner Losh * drm_crtc_init - Initialise a new CRTC object 408592ffb21SWarner Losh * @dev: DRM device 409592ffb21SWarner Losh * @crtc: CRTC object to init 410592ffb21SWarner Losh * @funcs: callbacks for the new CRTC 411592ffb21SWarner Losh * 412592ffb21SWarner Losh * LOCKING: 413592ffb21SWarner Losh * Takes mode_config lock. 414592ffb21SWarner Losh * 415592ffb21SWarner Losh * Inits a new object created as base part of an driver crtc object. 416592ffb21SWarner Losh * 417592ffb21SWarner Losh * RETURNS: 418592ffb21SWarner Losh * Zero on success, error code on failure. 419592ffb21SWarner Losh */ 420592ffb21SWarner Losh int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, 421592ffb21SWarner Losh const struct drm_crtc_funcs *funcs) 422592ffb21SWarner Losh { 423592ffb21SWarner Losh int ret; 424592ffb21SWarner Losh 425592ffb21SWarner Losh crtc->dev = dev; 426592ffb21SWarner Losh crtc->funcs = funcs; 427592ffb21SWarner Losh crtc->invert_dimensions = false; 428592ffb21SWarner Losh 429592ffb21SWarner Losh sx_xlock(&dev->mode_config.mutex); 430592ffb21SWarner Losh 431592ffb21SWarner Losh ret = drm_mode_object_get(dev, &crtc->base, DRM_MODE_OBJECT_CRTC); 432592ffb21SWarner Losh if (ret) 433592ffb21SWarner Losh goto out; 434592ffb21SWarner Losh 435592ffb21SWarner Losh crtc->base.properties = &crtc->properties; 436592ffb21SWarner Losh 437592ffb21SWarner Losh list_add_tail(&crtc->head, &dev->mode_config.crtc_list); 438592ffb21SWarner Losh dev->mode_config.num_crtc++; 439592ffb21SWarner Losh 440592ffb21SWarner Losh out: 441592ffb21SWarner Losh sx_xunlock(&dev->mode_config.mutex); 442592ffb21SWarner Losh 443592ffb21SWarner Losh return ret; 444592ffb21SWarner Losh } 445592ffb21SWarner Losh EXPORT_SYMBOL(drm_crtc_init); 446592ffb21SWarner Losh 447592ffb21SWarner Losh /** 448592ffb21SWarner Losh * drm_crtc_cleanup - Cleans up the core crtc usage. 449592ffb21SWarner Losh * @crtc: CRTC to cleanup 450592ffb21SWarner Losh * 451592ffb21SWarner Losh * LOCKING: 452592ffb21SWarner Losh * Caller must hold mode config lock. 453592ffb21SWarner Losh * 454592ffb21SWarner Losh * Cleanup @crtc. Removes from drm modesetting space 455592ffb21SWarner Losh * does NOT free object, caller does that. 456592ffb21SWarner Losh */ 457592ffb21SWarner Losh void drm_crtc_cleanup(struct drm_crtc *crtc) 458592ffb21SWarner Losh { 459592ffb21SWarner Losh struct drm_device *dev = crtc->dev; 460592ffb21SWarner Losh 461592ffb21SWarner Losh free(crtc->gamma_store, DRM_MEM_KMS); 462592ffb21SWarner Losh crtc->gamma_store = NULL; 463592ffb21SWarner Losh 464592ffb21SWarner Losh drm_mode_object_put(dev, &crtc->base); 465592ffb21SWarner Losh list_del(&crtc->head); 466592ffb21SWarner Losh dev->mode_config.num_crtc--; 467592ffb21SWarner Losh } 468592ffb21SWarner Losh EXPORT_SYMBOL(drm_crtc_cleanup); 469592ffb21SWarner Losh 470592ffb21SWarner Losh /** 471592ffb21SWarner Losh * drm_mode_probed_add - add a mode to a connector's probed mode list 472592ffb21SWarner Losh * @connector: connector the new mode 473592ffb21SWarner Losh * @mode: mode data 474592ffb21SWarner Losh * 475592ffb21SWarner Losh * LOCKING: 476592ffb21SWarner Losh * Caller must hold mode config lock. 477592ffb21SWarner Losh * 478592ffb21SWarner Losh * Add @mode to @connector's mode list for later use. 479592ffb21SWarner Losh */ 480592ffb21SWarner Losh void drm_mode_probed_add(struct drm_connector *connector, 481592ffb21SWarner Losh struct drm_display_mode *mode) 482592ffb21SWarner Losh { 483592ffb21SWarner Losh list_add(&mode->head, &connector->probed_modes); 484592ffb21SWarner Losh } 485592ffb21SWarner Losh EXPORT_SYMBOL(drm_mode_probed_add); 486592ffb21SWarner Losh 487592ffb21SWarner Losh /** 488592ffb21SWarner Losh * drm_mode_remove - remove and free a mode 489592ffb21SWarner Losh * @connector: connector list to modify 490592ffb21SWarner Losh * @mode: mode to remove 491592ffb21SWarner Losh * 492592ffb21SWarner Losh * LOCKING: 493592ffb21SWarner Losh * Caller must hold mode config lock. 494592ffb21SWarner Losh * 495592ffb21SWarner Losh * Remove @mode from @connector's mode list, then free it. 496592ffb21SWarner Losh */ 497592ffb21SWarner Losh void drm_mode_remove(struct drm_connector *connector, 498592ffb21SWarner Losh struct drm_display_mode *mode) 499592ffb21SWarner Losh { 500592ffb21SWarner Losh list_del(&mode->head); 501592ffb21SWarner Losh drm_mode_destroy(connector->dev, mode); 502592ffb21SWarner Losh } 503592ffb21SWarner Losh EXPORT_SYMBOL(drm_mode_remove); 504592ffb21SWarner Losh 505592ffb21SWarner Losh /** 506592ffb21SWarner Losh * drm_connector_init - Init a preallocated connector 507592ffb21SWarner Losh * @dev: DRM device 508592ffb21SWarner Losh * @connector: the connector to init 509592ffb21SWarner Losh * @funcs: callbacks for this connector 510592ffb21SWarner Losh * @name: user visible name of the connector 511592ffb21SWarner Losh * 512592ffb21SWarner Losh * LOCKING: 513592ffb21SWarner Losh * Takes mode config lock. 514592ffb21SWarner Losh * 515592ffb21SWarner Losh * Initialises a preallocated connector. Connectors should be 516592ffb21SWarner Losh * subclassed as part of driver connector objects. 517592ffb21SWarner Losh * 518592ffb21SWarner Losh * RETURNS: 519592ffb21SWarner Losh * Zero on success, error code on failure. 520592ffb21SWarner Losh */ 521592ffb21SWarner Losh int drm_connector_init(struct drm_device *dev, 522592ffb21SWarner Losh struct drm_connector *connector, 523592ffb21SWarner Losh const struct drm_connector_funcs *funcs, 524592ffb21SWarner Losh int connector_type) 525592ffb21SWarner Losh { 526592ffb21SWarner Losh int ret; 527592ffb21SWarner Losh 528592ffb21SWarner Losh sx_xlock(&dev->mode_config.mutex); 529592ffb21SWarner Losh 530592ffb21SWarner Losh ret = drm_mode_object_get(dev, &connector->base, DRM_MODE_OBJECT_CONNECTOR); 531592ffb21SWarner Losh if (ret) 532592ffb21SWarner Losh goto out; 533592ffb21SWarner Losh 534592ffb21SWarner Losh connector->base.properties = &connector->properties; 535592ffb21SWarner Losh connector->dev = dev; 536592ffb21SWarner Losh connector->funcs = funcs; 537592ffb21SWarner Losh connector->connector_type = connector_type; 538592ffb21SWarner Losh connector->connector_type_id = 539592ffb21SWarner Losh ++drm_connector_enum_list[connector_type].count; /* TODO */ 540592ffb21SWarner Losh INIT_LIST_HEAD(&connector->user_modes); 541592ffb21SWarner Losh INIT_LIST_HEAD(&connector->probed_modes); 542592ffb21SWarner Losh INIT_LIST_HEAD(&connector->modes); 543592ffb21SWarner Losh connector->edid_blob_ptr = NULL; 544592ffb21SWarner Losh connector->status = connector_status_unknown; 545592ffb21SWarner Losh 546592ffb21SWarner Losh list_add_tail(&connector->head, &dev->mode_config.connector_list); 547592ffb21SWarner Losh dev->mode_config.num_connector++; 548592ffb21SWarner Losh 549592ffb21SWarner Losh if (connector_type != DRM_MODE_CONNECTOR_VIRTUAL) 550592ffb21SWarner Losh drm_object_attach_property(&connector->base, 551592ffb21SWarner Losh dev->mode_config.edid_property, 552592ffb21SWarner Losh 0); 553592ffb21SWarner Losh 554592ffb21SWarner Losh drm_object_attach_property(&connector->base, 555592ffb21SWarner Losh dev->mode_config.dpms_property, 0); 556592ffb21SWarner Losh 557592ffb21SWarner Losh out: 558592ffb21SWarner Losh sx_xunlock(&dev->mode_config.mutex); 559592ffb21SWarner Losh 560592ffb21SWarner Losh return ret; 561592ffb21SWarner Losh } 562592ffb21SWarner Losh EXPORT_SYMBOL(drm_connector_init); 563592ffb21SWarner Losh 564592ffb21SWarner Losh /** 565592ffb21SWarner Losh * drm_connector_cleanup - cleans up an initialised connector 566592ffb21SWarner Losh * @connector: connector to cleanup 567592ffb21SWarner Losh * 568592ffb21SWarner Losh * LOCKING: 569592ffb21SWarner Losh * Takes mode config lock. 570592ffb21SWarner Losh * 571592ffb21SWarner Losh * Cleans up the connector but doesn't free the object. 572592ffb21SWarner Losh */ 573592ffb21SWarner Losh void drm_connector_cleanup(struct drm_connector *connector) 574592ffb21SWarner Losh { 575592ffb21SWarner Losh struct drm_device *dev = connector->dev; 576592ffb21SWarner Losh struct drm_display_mode *mode, *t; 577592ffb21SWarner Losh 578592ffb21SWarner Losh list_for_each_entry_safe(mode, t, &connector->probed_modes, head) 579592ffb21SWarner Losh drm_mode_remove(connector, mode); 580592ffb21SWarner Losh 581592ffb21SWarner Losh list_for_each_entry_safe(mode, t, &connector->modes, head) 582592ffb21SWarner Losh drm_mode_remove(connector, mode); 583592ffb21SWarner Losh 584592ffb21SWarner Losh list_for_each_entry_safe(mode, t, &connector->user_modes, head) 585592ffb21SWarner Losh drm_mode_remove(connector, mode); 586592ffb21SWarner Losh 587592ffb21SWarner Losh sx_xlock(&dev->mode_config.mutex); 588592ffb21SWarner Losh drm_mode_object_put(dev, &connector->base); 589592ffb21SWarner Losh list_del(&connector->head); 590592ffb21SWarner Losh dev->mode_config.num_connector--; 591592ffb21SWarner Losh sx_xunlock(&dev->mode_config.mutex); 592592ffb21SWarner Losh } 593592ffb21SWarner Losh EXPORT_SYMBOL(drm_connector_cleanup); 594592ffb21SWarner Losh 595592ffb21SWarner Losh void drm_connector_unplug_all(struct drm_device *dev) 596592ffb21SWarner Losh { 597592ffb21SWarner Losh #ifdef FREEBSD_NOTYET 598592ffb21SWarner Losh struct drm_connector *connector; 599592ffb21SWarner Losh 600592ffb21SWarner Losh /* taking the mode config mutex ends up in a clash with sysfs */ 601592ffb21SWarner Losh list_for_each_entry(connector, &dev->mode_config.connector_list, head) 602592ffb21SWarner Losh drm_sysfs_connector_remove(connector); 603592ffb21SWarner Losh #endif /* FREEBSD_NOTYET */ 604592ffb21SWarner Losh 605592ffb21SWarner Losh } 606592ffb21SWarner Losh EXPORT_SYMBOL(drm_connector_unplug_all); 607592ffb21SWarner Losh 608592ffb21SWarner Losh int drm_encoder_init(struct drm_device *dev, 609592ffb21SWarner Losh struct drm_encoder *encoder, 610592ffb21SWarner Losh const struct drm_encoder_funcs *funcs, 611592ffb21SWarner Losh int encoder_type) 612592ffb21SWarner Losh { 613592ffb21SWarner Losh int ret; 614592ffb21SWarner Losh 615592ffb21SWarner Losh sx_xlock(&dev->mode_config.mutex); 616592ffb21SWarner Losh 617592ffb21SWarner Losh ret = drm_mode_object_get(dev, &encoder->base, DRM_MODE_OBJECT_ENCODER); 618592ffb21SWarner Losh if (ret) 619592ffb21SWarner Losh goto out; 620592ffb21SWarner Losh 621592ffb21SWarner Losh encoder->dev = dev; 622592ffb21SWarner Losh encoder->encoder_type = encoder_type; 623592ffb21SWarner Losh encoder->funcs = funcs; 624592ffb21SWarner Losh 625592ffb21SWarner Losh list_add_tail(&encoder->head, &dev->mode_config.encoder_list); 626592ffb21SWarner Losh dev->mode_config.num_encoder++; 627592ffb21SWarner Losh 628592ffb21SWarner Losh out: 629592ffb21SWarner Losh sx_xunlock(&dev->mode_config.mutex); 630592ffb21SWarner Losh 631592ffb21SWarner Losh return ret; 632592ffb21SWarner Losh } 633592ffb21SWarner Losh EXPORT_SYMBOL(drm_encoder_init); 634592ffb21SWarner Losh 635592ffb21SWarner Losh void drm_encoder_cleanup(struct drm_encoder *encoder) 636592ffb21SWarner Losh { 637592ffb21SWarner Losh struct drm_device *dev = encoder->dev; 638592ffb21SWarner Losh sx_xlock(&dev->mode_config.mutex); 639592ffb21SWarner Losh drm_mode_object_put(dev, &encoder->base); 640592ffb21SWarner Losh list_del(&encoder->head); 641592ffb21SWarner Losh dev->mode_config.num_encoder--; 642592ffb21SWarner Losh sx_xunlock(&dev->mode_config.mutex); 643592ffb21SWarner Losh } 644592ffb21SWarner Losh EXPORT_SYMBOL(drm_encoder_cleanup); 645592ffb21SWarner Losh 646592ffb21SWarner Losh int drm_plane_init(struct drm_device *dev, struct drm_plane *plane, 647592ffb21SWarner Losh unsigned long possible_crtcs, 648592ffb21SWarner Losh const struct drm_plane_funcs *funcs, 649592ffb21SWarner Losh const uint32_t *formats, uint32_t format_count, 650592ffb21SWarner Losh bool priv) 651592ffb21SWarner Losh { 652592ffb21SWarner Losh int ret; 653592ffb21SWarner Losh 654592ffb21SWarner Losh sx_xlock(&dev->mode_config.mutex); 655592ffb21SWarner Losh 656592ffb21SWarner Losh ret = drm_mode_object_get(dev, &plane->base, DRM_MODE_OBJECT_PLANE); 657592ffb21SWarner Losh if (ret) 658592ffb21SWarner Losh goto out; 659592ffb21SWarner Losh 660592ffb21SWarner Losh plane->base.properties = &plane->properties; 661592ffb21SWarner Losh plane->dev = dev; 662592ffb21SWarner Losh plane->funcs = funcs; 663592ffb21SWarner Losh plane->format_types = malloc(sizeof(uint32_t) * format_count, 664592ffb21SWarner Losh DRM_MEM_KMS, M_WAITOK); 665592ffb21SWarner Losh memcpy(plane->format_types, formats, format_count * sizeof(uint32_t)); 666592ffb21SWarner Losh plane->format_count = format_count; 667592ffb21SWarner Losh plane->possible_crtcs = possible_crtcs; 668592ffb21SWarner Losh 669592ffb21SWarner Losh /* private planes are not exposed to userspace, but depending on 670592ffb21SWarner Losh * display hardware, might be convenient to allow sharing programming 671592ffb21SWarner Losh * for the scanout engine with the crtc implementation. 672592ffb21SWarner Losh */ 673592ffb21SWarner Losh if (!priv) { 674592ffb21SWarner Losh list_add_tail(&plane->head, &dev->mode_config.plane_list); 675592ffb21SWarner Losh dev->mode_config.num_plane++; 676592ffb21SWarner Losh } else { 677592ffb21SWarner Losh INIT_LIST_HEAD(&plane->head); 678592ffb21SWarner Losh } 679592ffb21SWarner Losh 680592ffb21SWarner Losh out: 681592ffb21SWarner Losh sx_xunlock(&dev->mode_config.mutex); 682592ffb21SWarner Losh 683592ffb21SWarner Losh return ret; 684592ffb21SWarner Losh } 685592ffb21SWarner Losh EXPORT_SYMBOL(drm_plane_init); 686592ffb21SWarner Losh 687592ffb21SWarner Losh void drm_plane_cleanup(struct drm_plane *plane) 688592ffb21SWarner Losh { 689592ffb21SWarner Losh struct drm_device *dev = plane->dev; 690592ffb21SWarner Losh 691592ffb21SWarner Losh sx_xlock(&dev->mode_config.mutex); 692592ffb21SWarner Losh free(plane->format_types, DRM_MEM_KMS); 693592ffb21SWarner Losh drm_mode_object_put(dev, &plane->base); 694592ffb21SWarner Losh /* if not added to a list, it must be a private plane */ 695592ffb21SWarner Losh if (!list_empty(&plane->head)) { 696592ffb21SWarner Losh list_del(&plane->head); 697592ffb21SWarner Losh dev->mode_config.num_plane--; 698592ffb21SWarner Losh } 699592ffb21SWarner Losh sx_xunlock(&dev->mode_config.mutex); 700592ffb21SWarner Losh } 701592ffb21SWarner Losh EXPORT_SYMBOL(drm_plane_cleanup); 702592ffb21SWarner Losh 703592ffb21SWarner Losh /** 704592ffb21SWarner Losh * drm_mode_create - create a new display mode 705592ffb21SWarner Losh * @dev: DRM device 706592ffb21SWarner Losh * 707592ffb21SWarner Losh * LOCKING: 708592ffb21SWarner Losh * Caller must hold DRM mode_config lock. 709592ffb21SWarner Losh * 710592ffb21SWarner Losh * Create a new drm_display_mode, give it an ID, and return it. 711592ffb21SWarner Losh * 712592ffb21SWarner Losh * RETURNS: 713592ffb21SWarner Losh * Pointer to new mode on success, NULL on error. 714592ffb21SWarner Losh */ 715592ffb21SWarner Losh struct drm_display_mode *drm_mode_create(struct drm_device *dev) 716592ffb21SWarner Losh { 717592ffb21SWarner Losh struct drm_display_mode *nmode; 718592ffb21SWarner Losh 719592ffb21SWarner Losh nmode = malloc(sizeof(struct drm_display_mode), DRM_MEM_KMS, 720592ffb21SWarner Losh M_WAITOK | M_ZERO); 721592ffb21SWarner Losh 722592ffb21SWarner Losh if (drm_mode_object_get(dev, &nmode->base, DRM_MODE_OBJECT_MODE)) { 723592ffb21SWarner Losh free(nmode, DRM_MEM_KMS); 724592ffb21SWarner Losh return NULL; 725592ffb21SWarner Losh } 726592ffb21SWarner Losh 727592ffb21SWarner Losh return nmode; 728592ffb21SWarner Losh } 729592ffb21SWarner Losh EXPORT_SYMBOL(drm_mode_create); 730592ffb21SWarner Losh 731592ffb21SWarner Losh /** 732592ffb21SWarner Losh * drm_mode_destroy - remove a mode 733592ffb21SWarner Losh * @dev: DRM device 734592ffb21SWarner Losh * @mode: mode to remove 735592ffb21SWarner Losh * 736592ffb21SWarner Losh * LOCKING: 737592ffb21SWarner Losh * Caller must hold mode config lock. 738592ffb21SWarner Losh * 739592ffb21SWarner Losh * Free @mode's unique identifier, then free it. 740592ffb21SWarner Losh */ 741592ffb21SWarner Losh void drm_mode_destroy(struct drm_device *dev, struct drm_display_mode *mode) 742592ffb21SWarner Losh { 743592ffb21SWarner Losh if (!mode) 744592ffb21SWarner Losh return; 745592ffb21SWarner Losh 746592ffb21SWarner Losh drm_mode_object_put(dev, &mode->base); 747592ffb21SWarner Losh 748592ffb21SWarner Losh free(mode, DRM_MEM_KMS); 749592ffb21SWarner Losh } 750592ffb21SWarner Losh EXPORT_SYMBOL(drm_mode_destroy); 751592ffb21SWarner Losh 752592ffb21SWarner Losh static int drm_mode_create_standard_connector_properties(struct drm_device *dev) 753592ffb21SWarner Losh { 754592ffb21SWarner Losh struct drm_property *edid; 755592ffb21SWarner Losh struct drm_property *dpms; 756592ffb21SWarner Losh 757592ffb21SWarner Losh /* 758592ffb21SWarner Losh * Standard properties (apply to all connectors) 759592ffb21SWarner Losh */ 760592ffb21SWarner Losh edid = drm_property_create(dev, DRM_MODE_PROP_BLOB | 761592ffb21SWarner Losh DRM_MODE_PROP_IMMUTABLE, 762592ffb21SWarner Losh "EDID", 0); 763592ffb21SWarner Losh dev->mode_config.edid_property = edid; 764592ffb21SWarner Losh 765592ffb21SWarner Losh dpms = drm_property_create_enum(dev, 0, 766592ffb21SWarner Losh "DPMS", drm_dpms_enum_list, 767592ffb21SWarner Losh ARRAY_SIZE(drm_dpms_enum_list)); 768592ffb21SWarner Losh dev->mode_config.dpms_property = dpms; 769592ffb21SWarner Losh 770592ffb21SWarner Losh return 0; 771592ffb21SWarner Losh } 772592ffb21SWarner Losh 773592ffb21SWarner Losh /** 774592ffb21SWarner Losh * drm_mode_create_dvi_i_properties - create DVI-I specific connector properties 775592ffb21SWarner Losh * @dev: DRM device 776592ffb21SWarner Losh * 777592ffb21SWarner Losh * Called by a driver the first time a DVI-I connector is made. 778592ffb21SWarner Losh */ 779592ffb21SWarner Losh int drm_mode_create_dvi_i_properties(struct drm_device *dev) 780592ffb21SWarner Losh { 781592ffb21SWarner Losh struct drm_property *dvi_i_selector; 782592ffb21SWarner Losh struct drm_property *dvi_i_subconnector; 783592ffb21SWarner Losh 784592ffb21SWarner Losh if (dev->mode_config.dvi_i_select_subconnector_property) 785592ffb21SWarner Losh return 0; 786592ffb21SWarner Losh 787592ffb21SWarner Losh dvi_i_selector = 788592ffb21SWarner Losh drm_property_create_enum(dev, 0, 789592ffb21SWarner Losh "select subconnector", 790592ffb21SWarner Losh drm_dvi_i_select_enum_list, 791592ffb21SWarner Losh ARRAY_SIZE(drm_dvi_i_select_enum_list)); 792592ffb21SWarner Losh dev->mode_config.dvi_i_select_subconnector_property = dvi_i_selector; 793592ffb21SWarner Losh 794592ffb21SWarner Losh dvi_i_subconnector = drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE, 795592ffb21SWarner Losh "subconnector", 796592ffb21SWarner Losh drm_dvi_i_subconnector_enum_list, 797592ffb21SWarner Losh ARRAY_SIZE(drm_dvi_i_subconnector_enum_list)); 798592ffb21SWarner Losh dev->mode_config.dvi_i_subconnector_property = dvi_i_subconnector; 799592ffb21SWarner Losh 800592ffb21SWarner Losh return 0; 801592ffb21SWarner Losh } 802592ffb21SWarner Losh EXPORT_SYMBOL(drm_mode_create_dvi_i_properties); 803592ffb21SWarner Losh 804592ffb21SWarner Losh /** 805592ffb21SWarner Losh * drm_create_tv_properties - create TV specific connector properties 806592ffb21SWarner Losh * @dev: DRM device 807592ffb21SWarner Losh * @num_modes: number of different TV formats (modes) supported 808592ffb21SWarner Losh * @modes: array of pointers to strings containing name of each format 809592ffb21SWarner Losh * 810592ffb21SWarner Losh * Called by a driver's TV initialization routine, this function creates 811592ffb21SWarner Losh * the TV specific connector properties for a given device. Caller is 812592ffb21SWarner Losh * responsible for allocating a list of format names and passing them to 813592ffb21SWarner Losh * this routine. 814592ffb21SWarner Losh */ 815592ffb21SWarner Losh int drm_mode_create_tv_properties(struct drm_device *dev, int num_modes, 816592ffb21SWarner Losh char *modes[]) 817592ffb21SWarner Losh { 818592ffb21SWarner Losh struct drm_property *tv_selector; 819592ffb21SWarner Losh struct drm_property *tv_subconnector; 820592ffb21SWarner Losh int i; 821592ffb21SWarner Losh 822592ffb21SWarner Losh if (dev->mode_config.tv_select_subconnector_property) 823592ffb21SWarner Losh return 0; 824592ffb21SWarner Losh 825592ffb21SWarner Losh /* 826592ffb21SWarner Losh * Basic connector properties 827592ffb21SWarner Losh */ 828592ffb21SWarner Losh tv_selector = drm_property_create_enum(dev, 0, 829592ffb21SWarner Losh "select subconnector", 830592ffb21SWarner Losh drm_tv_select_enum_list, 831592ffb21SWarner Losh ARRAY_SIZE(drm_tv_select_enum_list)); 832592ffb21SWarner Losh dev->mode_config.tv_select_subconnector_property = tv_selector; 833592ffb21SWarner Losh 834592ffb21SWarner Losh tv_subconnector = 835592ffb21SWarner Losh drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE, 836592ffb21SWarner Losh "subconnector", 837592ffb21SWarner Losh drm_tv_subconnector_enum_list, 838592ffb21SWarner Losh ARRAY_SIZE(drm_tv_subconnector_enum_list)); 839592ffb21SWarner Losh dev->mode_config.tv_subconnector_property = tv_subconnector; 840592ffb21SWarner Losh 841592ffb21SWarner Losh /* 842592ffb21SWarner Losh * Other, TV specific properties: margins & TV modes. 843592ffb21SWarner Losh */ 844592ffb21SWarner Losh dev->mode_config.tv_left_margin_property = 845592ffb21SWarner Losh drm_property_create_range(dev, 0, "left margin", 0, 100); 846592ffb21SWarner Losh 847592ffb21SWarner Losh dev->mode_config.tv_right_margin_property = 848592ffb21SWarner Losh drm_property_create_range(dev, 0, "right margin", 0, 100); 849592ffb21SWarner Losh 850592ffb21SWarner Losh dev->mode_config.tv_top_margin_property = 851592ffb21SWarner Losh drm_property_create_range(dev, 0, "top margin", 0, 100); 852592ffb21SWarner Losh 853592ffb21SWarner Losh dev->mode_config.tv_bottom_margin_property = 854592ffb21SWarner Losh drm_property_create_range(dev, 0, "bottom margin", 0, 100); 855592ffb21SWarner Losh 856592ffb21SWarner Losh dev->mode_config.tv_mode_property = 857592ffb21SWarner Losh drm_property_create(dev, DRM_MODE_PROP_ENUM, 858592ffb21SWarner Losh "mode", num_modes); 859592ffb21SWarner Losh for (i = 0; i < num_modes; i++) 860592ffb21SWarner Losh drm_property_add_enum(dev->mode_config.tv_mode_property, i, 861592ffb21SWarner Losh i, modes[i]); 862592ffb21SWarner Losh 863592ffb21SWarner Losh dev->mode_config.tv_brightness_property = 864592ffb21SWarner Losh drm_property_create_range(dev, 0, "brightness", 0, 100); 865592ffb21SWarner Losh 866592ffb21SWarner Losh dev->mode_config.tv_contrast_property = 867592ffb21SWarner Losh drm_property_create_range(dev, 0, "contrast", 0, 100); 868592ffb21SWarner Losh 869592ffb21SWarner Losh dev->mode_config.tv_flicker_reduction_property = 870592ffb21SWarner Losh drm_property_create_range(dev, 0, "flicker reduction", 0, 100); 871592ffb21SWarner Losh 872592ffb21SWarner Losh dev->mode_config.tv_overscan_property = 873592ffb21SWarner Losh drm_property_create_range(dev, 0, "overscan", 0, 100); 874592ffb21SWarner Losh 875592ffb21SWarner Losh dev->mode_config.tv_saturation_property = 876592ffb21SWarner Losh drm_property_create_range(dev, 0, "saturation", 0, 100); 877592ffb21SWarner Losh 878592ffb21SWarner Losh dev->mode_config.tv_hue_property = 879592ffb21SWarner Losh drm_property_create_range(dev, 0, "hue", 0, 100); 880592ffb21SWarner Losh 881592ffb21SWarner Losh return 0; 882592ffb21SWarner Losh } 883592ffb21SWarner Losh EXPORT_SYMBOL(drm_mode_create_tv_properties); 884592ffb21SWarner Losh 885592ffb21SWarner Losh /** 886592ffb21SWarner Losh * drm_mode_create_scaling_mode_property - create scaling mode property 887592ffb21SWarner Losh * @dev: DRM device 888592ffb21SWarner Losh * 889592ffb21SWarner Losh * Called by a driver the first time it's needed, must be attached to desired 890592ffb21SWarner Losh * connectors. 891592ffb21SWarner Losh */ 892592ffb21SWarner Losh int drm_mode_create_scaling_mode_property(struct drm_device *dev) 893592ffb21SWarner Losh { 894592ffb21SWarner Losh struct drm_property *scaling_mode; 895592ffb21SWarner Losh 896592ffb21SWarner Losh if (dev->mode_config.scaling_mode_property) 897592ffb21SWarner Losh return 0; 898592ffb21SWarner Losh 899592ffb21SWarner Losh scaling_mode = 900592ffb21SWarner Losh drm_property_create_enum(dev, 0, "scaling mode", 901592ffb21SWarner Losh drm_scaling_mode_enum_list, 902592ffb21SWarner Losh ARRAY_SIZE(drm_scaling_mode_enum_list)); 903592ffb21SWarner Losh 904592ffb21SWarner Losh dev->mode_config.scaling_mode_property = scaling_mode; 905592ffb21SWarner Losh 906592ffb21SWarner Losh return 0; 907592ffb21SWarner Losh } 908592ffb21SWarner Losh EXPORT_SYMBOL(drm_mode_create_scaling_mode_property); 909592ffb21SWarner Losh 910592ffb21SWarner Losh /** 911592ffb21SWarner Losh * drm_mode_create_dithering_property - create dithering property 912592ffb21SWarner Losh * @dev: DRM device 913592ffb21SWarner Losh * 914592ffb21SWarner Losh * Called by a driver the first time it's needed, must be attached to desired 915592ffb21SWarner Losh * connectors. 916592ffb21SWarner Losh */ 917592ffb21SWarner Losh int drm_mode_create_dithering_property(struct drm_device *dev) 918592ffb21SWarner Losh { 919592ffb21SWarner Losh struct drm_property *dithering_mode; 920592ffb21SWarner Losh 921592ffb21SWarner Losh if (dev->mode_config.dithering_mode_property) 922592ffb21SWarner Losh return 0; 923592ffb21SWarner Losh 924592ffb21SWarner Losh dithering_mode = 925592ffb21SWarner Losh drm_property_create_enum(dev, 0, "dithering", 926592ffb21SWarner Losh drm_dithering_mode_enum_list, 927592ffb21SWarner Losh ARRAY_SIZE(drm_dithering_mode_enum_list)); 928592ffb21SWarner Losh dev->mode_config.dithering_mode_property = dithering_mode; 929592ffb21SWarner Losh 930592ffb21SWarner Losh return 0; 931592ffb21SWarner Losh } 932592ffb21SWarner Losh EXPORT_SYMBOL(drm_mode_create_dithering_property); 933592ffb21SWarner Losh 934592ffb21SWarner Losh /** 935592ffb21SWarner Losh * drm_mode_create_dirty_property - create dirty property 936592ffb21SWarner Losh * @dev: DRM device 937592ffb21SWarner Losh * 938592ffb21SWarner Losh * Called by a driver the first time it's needed, must be attached to desired 939592ffb21SWarner Losh * connectors. 940592ffb21SWarner Losh */ 941592ffb21SWarner Losh int drm_mode_create_dirty_info_property(struct drm_device *dev) 942592ffb21SWarner Losh { 943592ffb21SWarner Losh struct drm_property *dirty_info; 944592ffb21SWarner Losh 945592ffb21SWarner Losh if (dev->mode_config.dirty_info_property) 946592ffb21SWarner Losh return 0; 947592ffb21SWarner Losh 948592ffb21SWarner Losh dirty_info = 949592ffb21SWarner Losh drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE, 950592ffb21SWarner Losh "dirty", 951592ffb21SWarner Losh drm_dirty_info_enum_list, 952592ffb21SWarner Losh ARRAY_SIZE(drm_dirty_info_enum_list)); 953592ffb21SWarner Losh dev->mode_config.dirty_info_property = dirty_info; 954592ffb21SWarner Losh 955592ffb21SWarner Losh return 0; 956592ffb21SWarner Losh } 957592ffb21SWarner Losh EXPORT_SYMBOL(drm_mode_create_dirty_info_property); 958592ffb21SWarner Losh 959592ffb21SWarner Losh /** 960592ffb21SWarner Losh * drm_mode_config_init - initialize DRM mode_configuration structure 961592ffb21SWarner Losh * @dev: DRM device 962592ffb21SWarner Losh * 963592ffb21SWarner Losh * LOCKING: 964592ffb21SWarner Losh * None, should happen single threaded at init time. 965592ffb21SWarner Losh * 966592ffb21SWarner Losh * Initialize @dev's mode_config structure, used for tracking the graphics 967592ffb21SWarner Losh * configuration of @dev. 968592ffb21SWarner Losh */ 969592ffb21SWarner Losh void drm_mode_config_init(struct drm_device *dev) 970592ffb21SWarner Losh { 971592ffb21SWarner Losh sx_init(&dev->mode_config.mutex, "kmslk"); 972592ffb21SWarner Losh INIT_LIST_HEAD(&dev->mode_config.fb_list); 973592ffb21SWarner Losh INIT_LIST_HEAD(&dev->mode_config.crtc_list); 974592ffb21SWarner Losh INIT_LIST_HEAD(&dev->mode_config.connector_list); 975592ffb21SWarner Losh INIT_LIST_HEAD(&dev->mode_config.encoder_list); 976592ffb21SWarner Losh INIT_LIST_HEAD(&dev->mode_config.property_list); 977592ffb21SWarner Losh INIT_LIST_HEAD(&dev->mode_config.property_blob_list); 978592ffb21SWarner Losh INIT_LIST_HEAD(&dev->mode_config.plane_list); 979592ffb21SWarner Losh drm_gem_names_init(&dev->mode_config.crtc_names); 980592ffb21SWarner Losh 981592ffb21SWarner Losh sx_xlock(&dev->mode_config.mutex); 982592ffb21SWarner Losh drm_mode_create_standard_connector_properties(dev); 983592ffb21SWarner Losh sx_xunlock(&dev->mode_config.mutex); 984592ffb21SWarner Losh 985592ffb21SWarner Losh /* Just to be sure */ 986592ffb21SWarner Losh dev->mode_config.num_fb = 0; 987592ffb21SWarner Losh dev->mode_config.num_connector = 0; 988592ffb21SWarner Losh dev->mode_config.num_crtc = 0; 989592ffb21SWarner Losh dev->mode_config.num_encoder = 0; 990592ffb21SWarner Losh } 991592ffb21SWarner Losh EXPORT_SYMBOL(drm_mode_config_init); 992592ffb21SWarner Losh 993592ffb21SWarner Losh int drm_mode_group_init(struct drm_device *dev, struct drm_mode_group *group) 994592ffb21SWarner Losh { 995592ffb21SWarner Losh uint32_t total_objects = 0; 996592ffb21SWarner Losh 997592ffb21SWarner Losh total_objects += dev->mode_config.num_crtc; 998592ffb21SWarner Losh total_objects += dev->mode_config.num_connector; 999592ffb21SWarner Losh total_objects += dev->mode_config.num_encoder; 1000592ffb21SWarner Losh 1001592ffb21SWarner Losh group->id_list = malloc(total_objects * sizeof(uint32_t), 1002592ffb21SWarner Losh DRM_MEM_KMS, M_WAITOK | M_ZERO); 1003592ffb21SWarner Losh group->num_crtcs = 0; 1004592ffb21SWarner Losh group->num_connectors = 0; 1005592ffb21SWarner Losh group->num_encoders = 0; 1006592ffb21SWarner Losh return 0; 1007592ffb21SWarner Losh } 1008592ffb21SWarner Losh 1009592ffb21SWarner Losh void drm_mode_group_free(struct drm_mode_group *group) 1010592ffb21SWarner Losh { 1011592ffb21SWarner Losh free(group->id_list, DRM_MEM_KMS); 1012592ffb21SWarner Losh group->id_list = NULL; 1013592ffb21SWarner Losh } 1014592ffb21SWarner Losh 1015592ffb21SWarner Losh int drm_mode_group_init_legacy_group(struct drm_device *dev, 1016592ffb21SWarner Losh struct drm_mode_group *group) 1017592ffb21SWarner Losh { 1018592ffb21SWarner Losh struct drm_crtc *crtc; 1019592ffb21SWarner Losh struct drm_encoder *encoder; 1020592ffb21SWarner Losh struct drm_connector *connector; 1021592ffb21SWarner Losh int ret; 1022592ffb21SWarner Losh 1023592ffb21SWarner Losh if ((ret = drm_mode_group_init(dev, group))) 1024592ffb21SWarner Losh return ret; 1025592ffb21SWarner Losh 1026592ffb21SWarner Losh list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) 1027592ffb21SWarner Losh group->id_list[group->num_crtcs++] = crtc->base.id; 1028592ffb21SWarner Losh 1029592ffb21SWarner Losh list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) 1030592ffb21SWarner Losh group->id_list[group->num_crtcs + group->num_encoders++] = 1031592ffb21SWarner Losh encoder->base.id; 1032592ffb21SWarner Losh 1033592ffb21SWarner Losh list_for_each_entry(connector, &dev->mode_config.connector_list, head) 1034592ffb21SWarner Losh group->id_list[group->num_crtcs + group->num_encoders + 1035592ffb21SWarner Losh group->num_connectors++] = connector->base.id; 1036592ffb21SWarner Losh 1037592ffb21SWarner Losh return 0; 1038592ffb21SWarner Losh } 1039592ffb21SWarner Losh EXPORT_SYMBOL(drm_mode_group_init_legacy_group); 1040592ffb21SWarner Losh 1041592ffb21SWarner Losh /** 1042592ffb21SWarner Losh * drm_mode_config_cleanup - free up DRM mode_config info 1043592ffb21SWarner Losh * @dev: DRM device 1044592ffb21SWarner Losh * 1045592ffb21SWarner Losh * LOCKING: 1046592ffb21SWarner Losh * Caller must hold mode config lock. 1047592ffb21SWarner Losh * 1048592ffb21SWarner Losh * Free up all the connectors and CRTCs associated with this DRM device, then 1049592ffb21SWarner Losh * free up the framebuffers and associated buffer objects. 1050592ffb21SWarner Losh * 1051592ffb21SWarner Losh * FIXME: cleanup any dangling user buffer objects too 1052592ffb21SWarner Losh */ 1053592ffb21SWarner Losh void drm_mode_config_cleanup(struct drm_device *dev) 1054592ffb21SWarner Losh { 1055592ffb21SWarner Losh struct drm_connector *connector, *ot; 1056592ffb21SWarner Losh struct drm_crtc *crtc, *ct; 1057592ffb21SWarner Losh struct drm_encoder *encoder, *enct; 1058592ffb21SWarner Losh struct drm_framebuffer *fb, *fbt; 1059592ffb21SWarner Losh struct drm_property *property, *pt; 1060592ffb21SWarner Losh struct drm_property_blob *blob, *bt; 1061592ffb21SWarner Losh struct drm_plane *plane, *plt; 1062592ffb21SWarner Losh 1063592ffb21SWarner Losh list_for_each_entry_safe(encoder, enct, &dev->mode_config.encoder_list, 1064592ffb21SWarner Losh head) { 1065592ffb21SWarner Losh encoder->funcs->destroy(encoder); 1066592ffb21SWarner Losh } 1067592ffb21SWarner Losh 1068592ffb21SWarner Losh list_for_each_entry_safe(connector, ot, 1069592ffb21SWarner Losh &dev->mode_config.connector_list, head) { 1070592ffb21SWarner Losh connector->funcs->destroy(connector); 1071592ffb21SWarner Losh } 1072592ffb21SWarner Losh 1073592ffb21SWarner Losh list_for_each_entry_safe(property, pt, &dev->mode_config.property_list, 1074592ffb21SWarner Losh head) { 1075592ffb21SWarner Losh drm_property_destroy(dev, property); 1076592ffb21SWarner Losh } 1077592ffb21SWarner Losh 1078592ffb21SWarner Losh list_for_each_entry_safe(blob, bt, &dev->mode_config.property_blob_list, 1079592ffb21SWarner Losh head) { 1080592ffb21SWarner Losh drm_property_destroy_blob(dev, blob); 1081592ffb21SWarner Losh } 1082592ffb21SWarner Losh 1083592ffb21SWarner Losh list_for_each_entry_safe(fb, fbt, &dev->mode_config.fb_list, head) { 1084592ffb21SWarner Losh drm_framebuffer_remove(fb); 1085592ffb21SWarner Losh } 1086592ffb21SWarner Losh 1087592ffb21SWarner Losh list_for_each_entry_safe(plane, plt, &dev->mode_config.plane_list, 1088592ffb21SWarner Losh head) { 1089592ffb21SWarner Losh plane->funcs->destroy(plane); 1090592ffb21SWarner Losh } 1091592ffb21SWarner Losh 1092592ffb21SWarner Losh list_for_each_entry_safe(crtc, ct, &dev->mode_config.crtc_list, head) { 1093592ffb21SWarner Losh crtc->funcs->destroy(crtc); 1094592ffb21SWarner Losh } 1095592ffb21SWarner Losh 1096592ffb21SWarner Losh drm_gem_names_fini(&dev->mode_config.crtc_names); 1097592ffb21SWarner Losh } 1098592ffb21SWarner Losh EXPORT_SYMBOL(drm_mode_config_cleanup); 1099592ffb21SWarner Losh 1100592ffb21SWarner Losh /** 1101592ffb21SWarner Losh * drm_crtc_convert_to_umode - convert a drm_display_mode into a modeinfo 1102592ffb21SWarner Losh * @out: drm_mode_modeinfo struct to return to the user 1103592ffb21SWarner Losh * @in: drm_display_mode to use 1104592ffb21SWarner Losh * 1105592ffb21SWarner Losh * LOCKING: 1106592ffb21SWarner Losh * None. 1107592ffb21SWarner Losh * 1108592ffb21SWarner Losh * Convert a drm_display_mode into a drm_mode_modeinfo structure to return to 1109592ffb21SWarner Losh * the user. 1110592ffb21SWarner Losh */ 1111592ffb21SWarner Losh static void drm_crtc_convert_to_umode(struct drm_mode_modeinfo *out, 1112592ffb21SWarner Losh const struct drm_display_mode *in) 1113592ffb21SWarner Losh { 1114592ffb21SWarner Losh if (in->hdisplay > USHRT_MAX || in->hsync_start > USHRT_MAX || 1115592ffb21SWarner Losh in->hsync_end > USHRT_MAX || in->htotal > USHRT_MAX || 1116592ffb21SWarner Losh in->hskew > USHRT_MAX || in->vdisplay > USHRT_MAX || 1117592ffb21SWarner Losh in->vsync_start > USHRT_MAX || in->vsync_end > USHRT_MAX || 1118592ffb21SWarner Losh in->vtotal > USHRT_MAX || in->vscan > USHRT_MAX) 1119592ffb21SWarner Losh DRM_WARNING("timing values too large for mode info\n"); 1120592ffb21SWarner Losh 1121592ffb21SWarner Losh out->clock = in->clock; 1122592ffb21SWarner Losh out->hdisplay = in->hdisplay; 1123592ffb21SWarner Losh out->hsync_start = in->hsync_start; 1124592ffb21SWarner Losh out->hsync_end = in->hsync_end; 1125592ffb21SWarner Losh out->htotal = in->htotal; 1126592ffb21SWarner Losh out->hskew = in->hskew; 1127592ffb21SWarner Losh out->vdisplay = in->vdisplay; 1128592ffb21SWarner Losh out->vsync_start = in->vsync_start; 1129592ffb21SWarner Losh out->vsync_end = in->vsync_end; 1130592ffb21SWarner Losh out->vtotal = in->vtotal; 1131592ffb21SWarner Losh out->vscan = in->vscan; 1132592ffb21SWarner Losh out->vrefresh = in->vrefresh; 1133592ffb21SWarner Losh out->flags = in->flags; 1134592ffb21SWarner Losh out->type = in->type; 1135592ffb21SWarner Losh strncpy(out->name, in->name, DRM_DISPLAY_MODE_LEN); 1136592ffb21SWarner Losh out->name[DRM_DISPLAY_MODE_LEN-1] = 0; 1137592ffb21SWarner Losh } 1138592ffb21SWarner Losh 1139592ffb21SWarner Losh /** 1140592ffb21SWarner Losh * drm_crtc_convert_to_umode - convert a modeinfo into a drm_display_mode 1141592ffb21SWarner Losh * @out: drm_display_mode to return to the user 1142592ffb21SWarner Losh * @in: drm_mode_modeinfo to use 1143592ffb21SWarner Losh * 1144592ffb21SWarner Losh * LOCKING: 1145592ffb21SWarner Losh * None. 1146592ffb21SWarner Losh * 1147592ffb21SWarner Losh * Convert a drm_mode_modeinfo into a drm_display_mode structure to return to 1148592ffb21SWarner Losh * the caller. 1149592ffb21SWarner Losh * 1150592ffb21SWarner Losh * RETURNS: 1151592ffb21SWarner Losh * Zero on success, errno on failure. 1152592ffb21SWarner Losh */ 1153592ffb21SWarner Losh static int drm_crtc_convert_umode(struct drm_display_mode *out, 1154592ffb21SWarner Losh const struct drm_mode_modeinfo *in) 1155592ffb21SWarner Losh { 1156592ffb21SWarner Losh if (in->clock > INT_MAX || in->vrefresh > INT_MAX) 1157592ffb21SWarner Losh return -ERANGE; 1158592ffb21SWarner Losh 1159592ffb21SWarner Losh out->clock = in->clock; 1160592ffb21SWarner Losh out->hdisplay = in->hdisplay; 1161592ffb21SWarner Losh out->hsync_start = in->hsync_start; 1162592ffb21SWarner Losh out->hsync_end = in->hsync_end; 1163592ffb21SWarner Losh out->htotal = in->htotal; 1164592ffb21SWarner Losh out->hskew = in->hskew; 1165592ffb21SWarner Losh out->vdisplay = in->vdisplay; 1166592ffb21SWarner Losh out->vsync_start = in->vsync_start; 1167592ffb21SWarner Losh out->vsync_end = in->vsync_end; 1168592ffb21SWarner Losh out->vtotal = in->vtotal; 1169592ffb21SWarner Losh out->vscan = in->vscan; 1170592ffb21SWarner Losh out->vrefresh = in->vrefresh; 1171592ffb21SWarner Losh out->flags = in->flags; 1172592ffb21SWarner Losh out->type = in->type; 1173592ffb21SWarner Losh strncpy(out->name, in->name, DRM_DISPLAY_MODE_LEN); 1174592ffb21SWarner Losh out->name[DRM_DISPLAY_MODE_LEN-1] = 0; 1175592ffb21SWarner Losh 1176592ffb21SWarner Losh return 0; 1177592ffb21SWarner Losh } 1178592ffb21SWarner Losh 1179592ffb21SWarner Losh /** 1180592ffb21SWarner Losh * drm_mode_getresources - get graphics configuration 1181592ffb21SWarner Losh * @inode: inode from the ioctl 1182592ffb21SWarner Losh * @filp: file * from the ioctl 1183592ffb21SWarner Losh * @cmd: cmd from ioctl 1184592ffb21SWarner Losh * @arg: arg from ioctl 1185592ffb21SWarner Losh * 1186592ffb21SWarner Losh * LOCKING: 1187592ffb21SWarner Losh * Takes mode config lock. 1188592ffb21SWarner Losh * 1189592ffb21SWarner Losh * Construct a set of configuration description structures and return 1190592ffb21SWarner Losh * them to the user, including CRTC, connector and framebuffer configuration. 1191592ffb21SWarner Losh * 1192592ffb21SWarner Losh * Called by the user via ioctl. 1193592ffb21SWarner Losh * 1194592ffb21SWarner Losh * RETURNS: 1195592ffb21SWarner Losh * Zero on success, errno on failure. 1196592ffb21SWarner Losh */ 1197592ffb21SWarner Losh int drm_mode_getresources(struct drm_device *dev, void *data, 1198592ffb21SWarner Losh struct drm_file *file_priv) 1199592ffb21SWarner Losh { 1200592ffb21SWarner Losh struct drm_mode_card_res *card_res = data; 1201592ffb21SWarner Losh struct list_head *lh; 1202592ffb21SWarner Losh struct drm_framebuffer *fb; 1203592ffb21SWarner Losh struct drm_connector *connector; 1204592ffb21SWarner Losh struct drm_crtc *crtc; 1205592ffb21SWarner Losh struct drm_encoder *encoder; 1206592ffb21SWarner Losh int ret = 0; 1207592ffb21SWarner Losh int connector_count = 0; 1208592ffb21SWarner Losh int crtc_count = 0; 1209592ffb21SWarner Losh int fb_count = 0; 1210592ffb21SWarner Losh int encoder_count = 0; 1211592ffb21SWarner Losh int copied = 0, i; 1212592ffb21SWarner Losh uint32_t __user *fb_id; 1213592ffb21SWarner Losh uint32_t __user *crtc_id; 1214592ffb21SWarner Losh uint32_t __user *connector_id; 1215592ffb21SWarner Losh uint32_t __user *encoder_id; 1216592ffb21SWarner Losh struct drm_mode_group *mode_group; 1217592ffb21SWarner Losh 1218592ffb21SWarner Losh if (!drm_core_check_feature(dev, DRIVER_MODESET)) 1219592ffb21SWarner Losh return -EINVAL; 1220592ffb21SWarner Losh 1221592ffb21SWarner Losh sx_xlock(&dev->mode_config.mutex); 1222592ffb21SWarner Losh 1223592ffb21SWarner Losh /* 1224592ffb21SWarner Losh * For the non-control nodes we need to limit the list of resources 1225592ffb21SWarner Losh * by IDs in the group list for this node 1226592ffb21SWarner Losh */ 1227592ffb21SWarner Losh list_for_each(lh, &file_priv->fbs) 1228592ffb21SWarner Losh fb_count++; 1229592ffb21SWarner Losh 1230592ffb21SWarner Losh mode_group = &file_priv->master->minor->mode_group; 1231592ffb21SWarner Losh if (file_priv->master->minor->type == DRM_MINOR_CONTROL) { 1232592ffb21SWarner Losh 1233592ffb21SWarner Losh list_for_each(lh, &dev->mode_config.crtc_list) 1234592ffb21SWarner Losh crtc_count++; 1235592ffb21SWarner Losh 1236592ffb21SWarner Losh list_for_each(lh, &dev->mode_config.connector_list) 1237592ffb21SWarner Losh connector_count++; 1238592ffb21SWarner Losh 1239592ffb21SWarner Losh list_for_each(lh, &dev->mode_config.encoder_list) 1240592ffb21SWarner Losh encoder_count++; 1241592ffb21SWarner Losh } else { 1242592ffb21SWarner Losh 1243592ffb21SWarner Losh crtc_count = mode_group->num_crtcs; 1244592ffb21SWarner Losh connector_count = mode_group->num_connectors; 1245592ffb21SWarner Losh encoder_count = mode_group->num_encoders; 1246592ffb21SWarner Losh } 1247592ffb21SWarner Losh 1248592ffb21SWarner Losh card_res->max_height = dev->mode_config.max_height; 1249592ffb21SWarner Losh card_res->min_height = dev->mode_config.min_height; 1250592ffb21SWarner Losh card_res->max_width = dev->mode_config.max_width; 1251592ffb21SWarner Losh card_res->min_width = dev->mode_config.min_width; 1252592ffb21SWarner Losh 1253592ffb21SWarner Losh /* handle this in 4 parts */ 1254592ffb21SWarner Losh /* FBs */ 1255592ffb21SWarner Losh if (card_res->count_fbs >= fb_count) { 1256592ffb21SWarner Losh copied = 0; 1257592ffb21SWarner Losh fb_id = (uint32_t __user *)(unsigned long)card_res->fb_id_ptr; 1258592ffb21SWarner Losh list_for_each_entry(fb, &file_priv->fbs, filp_head) { 1259592ffb21SWarner Losh if (put_user(fb->base.id, fb_id + copied)) { 1260592ffb21SWarner Losh ret = -EFAULT; 1261592ffb21SWarner Losh goto out; 1262592ffb21SWarner Losh } 1263592ffb21SWarner Losh copied++; 1264592ffb21SWarner Losh } 1265592ffb21SWarner Losh } 1266592ffb21SWarner Losh card_res->count_fbs = fb_count; 1267592ffb21SWarner Losh 1268592ffb21SWarner Losh /* CRTCs */ 1269592ffb21SWarner Losh if (card_res->count_crtcs >= crtc_count) { 1270592ffb21SWarner Losh copied = 0; 1271592ffb21SWarner Losh crtc_id = (uint32_t __user *)(unsigned long)card_res->crtc_id_ptr; 1272592ffb21SWarner Losh if (file_priv->master->minor->type == DRM_MINOR_CONTROL) { 1273592ffb21SWarner Losh list_for_each_entry(crtc, &dev->mode_config.crtc_list, 1274592ffb21SWarner Losh head) { 1275592ffb21SWarner Losh DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id); 1276592ffb21SWarner Losh if (put_user(crtc->base.id, crtc_id + copied)) { 1277592ffb21SWarner Losh ret = -EFAULT; 1278592ffb21SWarner Losh goto out; 1279592ffb21SWarner Losh } 1280592ffb21SWarner Losh copied++; 1281592ffb21SWarner Losh } 1282592ffb21SWarner Losh } else { 1283592ffb21SWarner Losh for (i = 0; i < mode_group->num_crtcs; i++) { 1284592ffb21SWarner Losh if (put_user(mode_group->id_list[i], 1285592ffb21SWarner Losh crtc_id + copied)) { 1286592ffb21SWarner Losh ret = -EFAULT; 1287592ffb21SWarner Losh goto out; 1288592ffb21SWarner Losh } 1289592ffb21SWarner Losh copied++; 1290592ffb21SWarner Losh } 1291592ffb21SWarner Losh } 1292592ffb21SWarner Losh } 1293592ffb21SWarner Losh card_res->count_crtcs = crtc_count; 1294592ffb21SWarner Losh 1295592ffb21SWarner Losh /* Encoders */ 1296592ffb21SWarner Losh if (card_res->count_encoders >= encoder_count) { 1297592ffb21SWarner Losh copied = 0; 1298592ffb21SWarner Losh encoder_id = (uint32_t __user *)(unsigned long)card_res->encoder_id_ptr; 1299592ffb21SWarner Losh if (file_priv->master->minor->type == DRM_MINOR_CONTROL) { 1300592ffb21SWarner Losh list_for_each_entry(encoder, 1301592ffb21SWarner Losh &dev->mode_config.encoder_list, 1302592ffb21SWarner Losh head) { 1303592ffb21SWarner Losh DRM_DEBUG_KMS("[ENCODER:%d:%s]\n", encoder->base.id, 1304592ffb21SWarner Losh drm_get_encoder_name(encoder)); 1305592ffb21SWarner Losh if (put_user(encoder->base.id, encoder_id + 1306592ffb21SWarner Losh copied)) { 1307592ffb21SWarner Losh ret = -EFAULT; 1308592ffb21SWarner Losh goto out; 1309592ffb21SWarner Losh } 1310592ffb21SWarner Losh copied++; 1311592ffb21SWarner Losh } 1312592ffb21SWarner Losh } else { 1313592ffb21SWarner Losh for (i = mode_group->num_crtcs; i < mode_group->num_crtcs + mode_group->num_encoders; i++) { 1314592ffb21SWarner Losh if (put_user(mode_group->id_list[i], 1315592ffb21SWarner Losh encoder_id + copied)) { 1316592ffb21SWarner Losh ret = -EFAULT; 1317592ffb21SWarner Losh goto out; 1318592ffb21SWarner Losh } 1319592ffb21SWarner Losh copied++; 1320592ffb21SWarner Losh } 1321592ffb21SWarner Losh 1322592ffb21SWarner Losh } 1323592ffb21SWarner Losh } 1324592ffb21SWarner Losh card_res->count_encoders = encoder_count; 1325592ffb21SWarner Losh 1326592ffb21SWarner Losh /* Connectors */ 1327592ffb21SWarner Losh if (card_res->count_connectors >= connector_count) { 1328592ffb21SWarner Losh copied = 0; 1329592ffb21SWarner Losh connector_id = (uint32_t __user *)(unsigned long)card_res->connector_id_ptr; 1330592ffb21SWarner Losh if (file_priv->master->minor->type == DRM_MINOR_CONTROL) { 1331592ffb21SWarner Losh list_for_each_entry(connector, 1332592ffb21SWarner Losh &dev->mode_config.connector_list, 1333592ffb21SWarner Losh head) { 1334592ffb21SWarner Losh DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", 1335592ffb21SWarner Losh connector->base.id, 1336592ffb21SWarner Losh drm_get_connector_name(connector)); 1337592ffb21SWarner Losh if (put_user(connector->base.id, 1338592ffb21SWarner Losh connector_id + copied)) { 1339592ffb21SWarner Losh ret = -EFAULT; 1340592ffb21SWarner Losh goto out; 1341592ffb21SWarner Losh } 1342592ffb21SWarner Losh copied++; 1343592ffb21SWarner Losh } 1344592ffb21SWarner Losh } else { 1345592ffb21SWarner Losh int start = mode_group->num_crtcs + 1346592ffb21SWarner Losh mode_group->num_encoders; 1347592ffb21SWarner Losh for (i = start; i < start + mode_group->num_connectors; i++) { 1348592ffb21SWarner Losh if (put_user(mode_group->id_list[i], 1349592ffb21SWarner Losh connector_id + copied)) { 1350592ffb21SWarner Losh ret = -EFAULT; 1351592ffb21SWarner Losh goto out; 1352592ffb21SWarner Losh } 1353592ffb21SWarner Losh copied++; 1354592ffb21SWarner Losh } 1355592ffb21SWarner Losh } 1356592ffb21SWarner Losh } 1357592ffb21SWarner Losh card_res->count_connectors = connector_count; 1358592ffb21SWarner Losh 1359592ffb21SWarner Losh DRM_DEBUG_KMS("CRTC[%d] CONNECTORS[%d] ENCODERS[%d]\n", card_res->count_crtcs, 1360592ffb21SWarner Losh card_res->count_connectors, card_res->count_encoders); 1361592ffb21SWarner Losh 1362592ffb21SWarner Losh out: 1363592ffb21SWarner Losh sx_xunlock(&dev->mode_config.mutex); 1364592ffb21SWarner Losh return ret; 1365592ffb21SWarner Losh } 1366592ffb21SWarner Losh 1367592ffb21SWarner Losh /** 1368592ffb21SWarner Losh * drm_mode_getcrtc - get CRTC configuration 1369592ffb21SWarner Losh * @inode: inode from the ioctl 1370592ffb21SWarner Losh * @filp: file * from the ioctl 1371592ffb21SWarner Losh * @cmd: cmd from ioctl 1372592ffb21SWarner Losh * @arg: arg from ioctl 1373592ffb21SWarner Losh * 1374592ffb21SWarner Losh * LOCKING: 1375592ffb21SWarner Losh * Takes mode config lock. 1376592ffb21SWarner Losh * 1377592ffb21SWarner Losh * Construct a CRTC configuration structure to return to the user. 1378592ffb21SWarner Losh * 1379592ffb21SWarner Losh * Called by the user via ioctl. 1380592ffb21SWarner Losh * 1381592ffb21SWarner Losh * RETURNS: 1382592ffb21SWarner Losh * Zero on success, errno on failure. 1383592ffb21SWarner Losh */ 1384592ffb21SWarner Losh int drm_mode_getcrtc(struct drm_device *dev, 1385592ffb21SWarner Losh void *data, struct drm_file *file_priv) 1386592ffb21SWarner Losh { 1387592ffb21SWarner Losh struct drm_mode_crtc *crtc_resp = data; 1388592ffb21SWarner Losh struct drm_crtc *crtc; 1389592ffb21SWarner Losh struct drm_mode_object *obj; 1390592ffb21SWarner Losh int ret = 0; 1391592ffb21SWarner Losh 1392592ffb21SWarner Losh if (!drm_core_check_feature(dev, DRIVER_MODESET)) 1393592ffb21SWarner Losh return -EINVAL; 1394592ffb21SWarner Losh 1395592ffb21SWarner Losh sx_xlock(&dev->mode_config.mutex); 1396592ffb21SWarner Losh 1397592ffb21SWarner Losh obj = drm_mode_object_find(dev, crtc_resp->crtc_id, 1398592ffb21SWarner Losh DRM_MODE_OBJECT_CRTC); 1399592ffb21SWarner Losh if (!obj) { 1400592ffb21SWarner Losh ret = -EINVAL; 1401592ffb21SWarner Losh goto out; 1402592ffb21SWarner Losh } 1403592ffb21SWarner Losh crtc = obj_to_crtc(obj); 1404592ffb21SWarner Losh 1405592ffb21SWarner Losh crtc_resp->x = crtc->x; 1406592ffb21SWarner Losh crtc_resp->y = crtc->y; 1407592ffb21SWarner Losh crtc_resp->gamma_size = crtc->gamma_size; 1408592ffb21SWarner Losh if (crtc->fb) 1409592ffb21SWarner Losh crtc_resp->fb_id = crtc->fb->base.id; 1410592ffb21SWarner Losh else 1411592ffb21SWarner Losh crtc_resp->fb_id = 0; 1412592ffb21SWarner Losh 1413592ffb21SWarner Losh if (crtc->enabled) { 1414592ffb21SWarner Losh 1415592ffb21SWarner Losh drm_crtc_convert_to_umode(&crtc_resp->mode, &crtc->mode); 1416592ffb21SWarner Losh crtc_resp->mode_valid = 1; 1417592ffb21SWarner Losh 1418592ffb21SWarner Losh } else { 1419592ffb21SWarner Losh crtc_resp->mode_valid = 0; 1420592ffb21SWarner Losh } 1421592ffb21SWarner Losh 1422592ffb21SWarner Losh out: 1423592ffb21SWarner Losh sx_xunlock(&dev->mode_config.mutex); 1424592ffb21SWarner Losh return ret; 1425592ffb21SWarner Losh } 1426592ffb21SWarner Losh 1427592ffb21SWarner Losh /** 1428592ffb21SWarner Losh * drm_mode_getconnector - get connector configuration 1429592ffb21SWarner Losh * @inode: inode from the ioctl 1430592ffb21SWarner Losh * @filp: file * from the ioctl 1431592ffb21SWarner Losh * @cmd: cmd from ioctl 1432592ffb21SWarner Losh * @arg: arg from ioctl 1433592ffb21SWarner Losh * 1434592ffb21SWarner Losh * LOCKING: 1435592ffb21SWarner Losh * Takes mode config lock. 1436592ffb21SWarner Losh * 1437592ffb21SWarner Losh * Construct a connector configuration structure to return to the user. 1438592ffb21SWarner Losh * 1439592ffb21SWarner Losh * Called by the user via ioctl. 1440592ffb21SWarner Losh * 1441592ffb21SWarner Losh * RETURNS: 1442592ffb21SWarner Losh * Zero on success, errno on failure. 1443592ffb21SWarner Losh */ 1444592ffb21SWarner Losh int drm_mode_getconnector(struct drm_device *dev, void *data, 1445592ffb21SWarner Losh struct drm_file *file_priv) 1446592ffb21SWarner Losh { 1447592ffb21SWarner Losh struct drm_mode_get_connector *out_resp = data; 1448592ffb21SWarner Losh struct drm_mode_object *obj; 1449592ffb21SWarner Losh struct drm_connector *connector; 1450592ffb21SWarner Losh struct drm_display_mode *mode; 1451592ffb21SWarner Losh int mode_count = 0; 1452592ffb21SWarner Losh int props_count = 0; 1453592ffb21SWarner Losh int encoders_count = 0; 1454592ffb21SWarner Losh int ret = 0; 1455592ffb21SWarner Losh int copied = 0; 1456592ffb21SWarner Losh int i; 1457592ffb21SWarner Losh struct drm_mode_modeinfo u_mode; 1458592ffb21SWarner Losh struct drm_mode_modeinfo __user *mode_ptr; 1459592ffb21SWarner Losh uint32_t __user *prop_ptr; 1460592ffb21SWarner Losh uint64_t __user *prop_values; 1461592ffb21SWarner Losh uint32_t __user *encoder_ptr; 1462592ffb21SWarner Losh 1463592ffb21SWarner Losh if (!drm_core_check_feature(dev, DRIVER_MODESET)) 1464592ffb21SWarner Losh return -EINVAL; 1465592ffb21SWarner Losh 1466592ffb21SWarner Losh memset(&u_mode, 0, sizeof(struct drm_mode_modeinfo)); 1467592ffb21SWarner Losh 1468592ffb21SWarner Losh DRM_DEBUG_KMS("[CONNECTOR:%d:?]\n", out_resp->connector_id); 1469592ffb21SWarner Losh 1470592ffb21SWarner Losh sx_xlock(&dev->mode_config.mutex); 1471592ffb21SWarner Losh 1472592ffb21SWarner Losh obj = drm_mode_object_find(dev, out_resp->connector_id, 1473592ffb21SWarner Losh DRM_MODE_OBJECT_CONNECTOR); 1474592ffb21SWarner Losh if (!obj) { 1475592ffb21SWarner Losh ret = -EINVAL; 1476592ffb21SWarner Losh goto out; 1477592ffb21SWarner Losh } 1478592ffb21SWarner Losh connector = obj_to_connector(obj); 1479592ffb21SWarner Losh 1480592ffb21SWarner Losh props_count = connector->properties.count; 1481592ffb21SWarner Losh 1482592ffb21SWarner Losh for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { 1483592ffb21SWarner Losh if (connector->encoder_ids[i] != 0) { 1484592ffb21SWarner Losh encoders_count++; 1485592ffb21SWarner Losh } 1486592ffb21SWarner Losh } 1487592ffb21SWarner Losh 1488592ffb21SWarner Losh if (out_resp->count_modes == 0) { 1489592ffb21SWarner Losh connector->funcs->fill_modes(connector, 1490592ffb21SWarner Losh dev->mode_config.max_width, 1491592ffb21SWarner Losh dev->mode_config.max_height); 1492592ffb21SWarner Losh } 1493592ffb21SWarner Losh 1494592ffb21SWarner Losh /* delayed so we get modes regardless of pre-fill_modes state */ 1495592ffb21SWarner Losh list_for_each_entry(mode, &connector->modes, head) 1496592ffb21SWarner Losh mode_count++; 1497592ffb21SWarner Losh 1498592ffb21SWarner Losh out_resp->connector_id = connector->base.id; 1499592ffb21SWarner Losh out_resp->connector_type = connector->connector_type; 1500592ffb21SWarner Losh out_resp->connector_type_id = connector->connector_type_id; 1501592ffb21SWarner Losh out_resp->mm_width = connector->display_info.width_mm; 1502592ffb21SWarner Losh out_resp->mm_height = connector->display_info.height_mm; 1503592ffb21SWarner Losh out_resp->subpixel = connector->display_info.subpixel_order; 1504592ffb21SWarner Losh out_resp->connection = connector->status; 1505592ffb21SWarner Losh if (connector->encoder) 1506592ffb21SWarner Losh out_resp->encoder_id = connector->encoder->base.id; 1507592ffb21SWarner Losh else 1508592ffb21SWarner Losh out_resp->encoder_id = 0; 1509592ffb21SWarner Losh 1510592ffb21SWarner Losh /* 1511592ffb21SWarner Losh * This ioctl is called twice, once to determine how much space is 1512592ffb21SWarner Losh * needed, and the 2nd time to fill it. 1513592ffb21SWarner Losh */ 1514592ffb21SWarner Losh if ((out_resp->count_modes >= mode_count) && mode_count) { 1515592ffb21SWarner Losh copied = 0; 1516592ffb21SWarner Losh mode_ptr = (struct drm_mode_modeinfo __user *)(unsigned long)out_resp->modes_ptr; 1517592ffb21SWarner Losh list_for_each_entry(mode, &connector->modes, head) { 1518592ffb21SWarner Losh drm_crtc_convert_to_umode(&u_mode, mode); 1519592ffb21SWarner Losh if (copy_to_user(mode_ptr + copied, 1520592ffb21SWarner Losh &u_mode, sizeof(u_mode))) { 1521592ffb21SWarner Losh ret = -EFAULT; 1522592ffb21SWarner Losh goto out; 1523592ffb21SWarner Losh } 1524592ffb21SWarner Losh copied++; 1525592ffb21SWarner Losh } 1526592ffb21SWarner Losh } 1527592ffb21SWarner Losh out_resp->count_modes = mode_count; 1528592ffb21SWarner Losh 1529592ffb21SWarner Losh if ((out_resp->count_props >= props_count) && props_count) { 1530592ffb21SWarner Losh copied = 0; 1531592ffb21SWarner Losh prop_ptr = (uint32_t __user *)(unsigned long)(out_resp->props_ptr); 1532592ffb21SWarner Losh prop_values = (uint64_t __user *)(unsigned long)(out_resp->prop_values_ptr); 1533592ffb21SWarner Losh for (i = 0; i < connector->properties.count; i++) { 1534592ffb21SWarner Losh if (put_user(connector->properties.ids[i], 1535592ffb21SWarner Losh prop_ptr + copied)) { 1536592ffb21SWarner Losh ret = -EFAULT; 1537592ffb21SWarner Losh goto out; 1538592ffb21SWarner Losh } 1539592ffb21SWarner Losh 1540592ffb21SWarner Losh if (put_user(connector->properties.values[i], 1541592ffb21SWarner Losh prop_values + copied)) { 1542592ffb21SWarner Losh ret = -EFAULT; 1543592ffb21SWarner Losh goto out; 1544592ffb21SWarner Losh } 1545592ffb21SWarner Losh copied++; 1546592ffb21SWarner Losh } 1547592ffb21SWarner Losh } 1548592ffb21SWarner Losh out_resp->count_props = props_count; 1549592ffb21SWarner Losh 1550592ffb21SWarner Losh if ((out_resp->count_encoders >= encoders_count) && encoders_count) { 1551592ffb21SWarner Losh copied = 0; 1552592ffb21SWarner Losh encoder_ptr = (uint32_t __user *)(unsigned long)(out_resp->encoders_ptr); 1553592ffb21SWarner Losh for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { 1554592ffb21SWarner Losh if (connector->encoder_ids[i] != 0) { 1555592ffb21SWarner Losh if (put_user(connector->encoder_ids[i], 1556592ffb21SWarner Losh encoder_ptr + copied)) { 1557592ffb21SWarner Losh ret = -EFAULT; 1558592ffb21SWarner Losh goto out; 1559592ffb21SWarner Losh } 1560592ffb21SWarner Losh copied++; 1561592ffb21SWarner Losh } 1562592ffb21SWarner Losh } 1563592ffb21SWarner Losh } 1564592ffb21SWarner Losh out_resp->count_encoders = encoders_count; 1565592ffb21SWarner Losh 1566592ffb21SWarner Losh out: 1567592ffb21SWarner Losh sx_xunlock(&dev->mode_config.mutex); 1568592ffb21SWarner Losh return ret; 1569592ffb21SWarner Losh } 1570592ffb21SWarner Losh 1571592ffb21SWarner Losh int drm_mode_getencoder(struct drm_device *dev, void *data, 1572592ffb21SWarner Losh struct drm_file *file_priv) 1573592ffb21SWarner Losh { 1574592ffb21SWarner Losh struct drm_mode_get_encoder *enc_resp = data; 1575592ffb21SWarner Losh struct drm_mode_object *obj; 1576592ffb21SWarner Losh struct drm_encoder *encoder; 1577592ffb21SWarner Losh int ret = 0; 1578592ffb21SWarner Losh 1579592ffb21SWarner Losh if (!drm_core_check_feature(dev, DRIVER_MODESET)) 1580592ffb21SWarner Losh return -EINVAL; 1581592ffb21SWarner Losh 1582592ffb21SWarner Losh sx_xlock(&dev->mode_config.mutex); 1583592ffb21SWarner Losh obj = drm_mode_object_find(dev, enc_resp->encoder_id, 1584592ffb21SWarner Losh DRM_MODE_OBJECT_ENCODER); 1585592ffb21SWarner Losh if (!obj) { 1586592ffb21SWarner Losh ret = -EINVAL; 1587592ffb21SWarner Losh goto out; 1588592ffb21SWarner Losh } 1589592ffb21SWarner Losh encoder = obj_to_encoder(obj); 1590592ffb21SWarner Losh 1591592ffb21SWarner Losh if (encoder->crtc) 1592592ffb21SWarner Losh enc_resp->crtc_id = encoder->crtc->base.id; 1593592ffb21SWarner Losh else 1594592ffb21SWarner Losh enc_resp->crtc_id = 0; 1595592ffb21SWarner Losh enc_resp->encoder_type = encoder->encoder_type; 1596592ffb21SWarner Losh enc_resp->encoder_id = encoder->base.id; 1597592ffb21SWarner Losh enc_resp->possible_crtcs = encoder->possible_crtcs; 1598592ffb21SWarner Losh enc_resp->possible_clones = encoder->possible_clones; 1599592ffb21SWarner Losh 1600592ffb21SWarner Losh out: 1601592ffb21SWarner Losh sx_xunlock(&dev->mode_config.mutex); 1602592ffb21SWarner Losh return ret; 1603592ffb21SWarner Losh } 1604592ffb21SWarner Losh 1605592ffb21SWarner Losh /** 1606592ffb21SWarner Losh * drm_mode_getplane_res - get plane info 1607592ffb21SWarner Losh * @dev: DRM device 1608592ffb21SWarner Losh * @data: ioctl data 1609592ffb21SWarner Losh * @file_priv: DRM file info 1610592ffb21SWarner Losh * 1611592ffb21SWarner Losh * LOCKING: 1612592ffb21SWarner Losh * Takes mode config lock. 1613592ffb21SWarner Losh * 1614592ffb21SWarner Losh * Return an plane count and set of IDs. 1615592ffb21SWarner Losh */ 1616592ffb21SWarner Losh int drm_mode_getplane_res(struct drm_device *dev, void *data, 1617592ffb21SWarner Losh struct drm_file *file_priv) 1618592ffb21SWarner Losh { 1619592ffb21SWarner Losh struct drm_mode_get_plane_res *plane_resp = data; 1620592ffb21SWarner Losh struct drm_mode_config *config; 1621592ffb21SWarner Losh struct drm_plane *plane; 1622592ffb21SWarner Losh uint32_t __user *plane_ptr; 1623592ffb21SWarner Losh int copied = 0, ret = 0; 1624592ffb21SWarner Losh 1625592ffb21SWarner Losh if (!drm_core_check_feature(dev, DRIVER_MODESET)) 1626592ffb21SWarner Losh return -EINVAL; 1627592ffb21SWarner Losh 1628592ffb21SWarner Losh sx_xlock(&dev->mode_config.mutex); 1629592ffb21SWarner Losh config = &dev->mode_config; 1630592ffb21SWarner Losh 1631592ffb21SWarner Losh /* 1632592ffb21SWarner Losh * This ioctl is called twice, once to determine how much space is 1633592ffb21SWarner Losh * needed, and the 2nd time to fill it. 1634592ffb21SWarner Losh */ 1635592ffb21SWarner Losh if (config->num_plane && 1636592ffb21SWarner Losh (plane_resp->count_planes >= config->num_plane)) { 1637592ffb21SWarner Losh plane_ptr = (uint32_t __user *)(unsigned long)plane_resp->plane_id_ptr; 1638592ffb21SWarner Losh 1639592ffb21SWarner Losh list_for_each_entry(plane, &config->plane_list, head) { 1640592ffb21SWarner Losh if (put_user(plane->base.id, plane_ptr + copied)) { 1641592ffb21SWarner Losh ret = -EFAULT; 1642592ffb21SWarner Losh goto out; 1643592ffb21SWarner Losh } 1644592ffb21SWarner Losh copied++; 1645592ffb21SWarner Losh } 1646592ffb21SWarner Losh } 1647592ffb21SWarner Losh plane_resp->count_planes = config->num_plane; 1648592ffb21SWarner Losh 1649592ffb21SWarner Losh out: 1650592ffb21SWarner Losh sx_xunlock(&dev->mode_config.mutex); 1651592ffb21SWarner Losh return ret; 1652592ffb21SWarner Losh } 1653592ffb21SWarner Losh 1654592ffb21SWarner Losh /** 1655592ffb21SWarner Losh * drm_mode_getplane - get plane info 1656592ffb21SWarner Losh * @dev: DRM device 1657592ffb21SWarner Losh * @data: ioctl data 1658592ffb21SWarner Losh * @file_priv: DRM file info 1659592ffb21SWarner Losh * 1660592ffb21SWarner Losh * LOCKING: 1661592ffb21SWarner Losh * Takes mode config lock. 1662592ffb21SWarner Losh * 1663592ffb21SWarner Losh * Return plane info, including formats supported, gamma size, any 1664592ffb21SWarner Losh * current fb, etc. 1665592ffb21SWarner Losh */ 1666592ffb21SWarner Losh int drm_mode_getplane(struct drm_device *dev, void *data, 1667592ffb21SWarner Losh struct drm_file *file_priv) 1668592ffb21SWarner Losh { 1669592ffb21SWarner Losh struct drm_mode_get_plane *plane_resp = data; 1670592ffb21SWarner Losh struct drm_mode_object *obj; 1671592ffb21SWarner Losh struct drm_plane *plane; 1672592ffb21SWarner Losh uint32_t __user *format_ptr; 1673592ffb21SWarner Losh int ret = 0; 1674592ffb21SWarner Losh 1675592ffb21SWarner Losh if (!drm_core_check_feature(dev, DRIVER_MODESET)) 1676592ffb21SWarner Losh return -EINVAL; 1677592ffb21SWarner Losh 1678592ffb21SWarner Losh sx_xlock(&dev->mode_config.mutex); 1679592ffb21SWarner Losh obj = drm_mode_object_find(dev, plane_resp->plane_id, 1680592ffb21SWarner Losh DRM_MODE_OBJECT_PLANE); 1681592ffb21SWarner Losh if (!obj) { 1682592ffb21SWarner Losh ret = -ENOENT; 1683592ffb21SWarner Losh goto out; 1684592ffb21SWarner Losh } 1685592ffb21SWarner Losh plane = obj_to_plane(obj); 1686592ffb21SWarner Losh 1687592ffb21SWarner Losh if (plane->crtc) 1688592ffb21SWarner Losh plane_resp->crtc_id = plane->crtc->base.id; 1689592ffb21SWarner Losh else 1690592ffb21SWarner Losh plane_resp->crtc_id = 0; 1691592ffb21SWarner Losh 1692592ffb21SWarner Losh if (plane->fb) 1693592ffb21SWarner Losh plane_resp->fb_id = plane->fb->base.id; 1694592ffb21SWarner Losh else 1695592ffb21SWarner Losh plane_resp->fb_id = 0; 1696592ffb21SWarner Losh 1697592ffb21SWarner Losh plane_resp->plane_id = plane->base.id; 1698592ffb21SWarner Losh plane_resp->possible_crtcs = plane->possible_crtcs; 1699592ffb21SWarner Losh plane_resp->gamma_size = plane->gamma_size; 1700592ffb21SWarner Losh 1701592ffb21SWarner Losh /* 1702592ffb21SWarner Losh * This ioctl is called twice, once to determine how much space is 1703592ffb21SWarner Losh * needed, and the 2nd time to fill it. 1704592ffb21SWarner Losh */ 1705592ffb21SWarner Losh if (plane->format_count && 1706592ffb21SWarner Losh (plane_resp->count_format_types >= plane->format_count)) { 1707592ffb21SWarner Losh format_ptr = (uint32_t __user *)(unsigned long)plane_resp->format_type_ptr; 1708592ffb21SWarner Losh if (copy_to_user(format_ptr, 1709592ffb21SWarner Losh plane->format_types, 1710592ffb21SWarner Losh sizeof(uint32_t) * plane->format_count)) { 1711592ffb21SWarner Losh ret = -EFAULT; 1712592ffb21SWarner Losh goto out; 1713592ffb21SWarner Losh } 1714592ffb21SWarner Losh } 1715592ffb21SWarner Losh plane_resp->count_format_types = plane->format_count; 1716592ffb21SWarner Losh 1717592ffb21SWarner Losh out: 1718592ffb21SWarner Losh sx_xunlock(&dev->mode_config.mutex); 1719592ffb21SWarner Losh return ret; 1720592ffb21SWarner Losh } 1721592ffb21SWarner Losh 1722592ffb21SWarner Losh /** 1723592ffb21SWarner Losh * drm_mode_setplane - set up or tear down an plane 1724592ffb21SWarner Losh * @dev: DRM device 1725592ffb21SWarner Losh * @data: ioctl data* 1726592ffb21SWarner Losh * @file_prive: DRM file info 1727592ffb21SWarner Losh * 1728592ffb21SWarner Losh * LOCKING: 1729592ffb21SWarner Losh * Takes mode config lock. 1730592ffb21SWarner Losh * 1731592ffb21SWarner Losh * Set plane info, including placement, fb, scaling, and other factors. 1732592ffb21SWarner Losh * Or pass a NULL fb to disable. 1733592ffb21SWarner Losh */ 1734592ffb21SWarner Losh int drm_mode_setplane(struct drm_device *dev, void *data, 1735592ffb21SWarner Losh struct drm_file *file_priv) 1736592ffb21SWarner Losh { 1737592ffb21SWarner Losh struct drm_mode_set_plane *plane_req = data; 1738592ffb21SWarner Losh struct drm_mode_object *obj; 1739592ffb21SWarner Losh struct drm_plane *plane; 1740592ffb21SWarner Losh struct drm_crtc *crtc; 1741592ffb21SWarner Losh struct drm_framebuffer *fb; 1742592ffb21SWarner Losh int ret = 0; 1743592ffb21SWarner Losh unsigned int fb_width, fb_height; 1744592ffb21SWarner Losh int i; 1745592ffb21SWarner Losh 1746592ffb21SWarner Losh if (!drm_core_check_feature(dev, DRIVER_MODESET)) 1747592ffb21SWarner Losh return -EINVAL; 1748592ffb21SWarner Losh 1749592ffb21SWarner Losh sx_xlock(&dev->mode_config.mutex); 1750592ffb21SWarner Losh 1751592ffb21SWarner Losh /* 1752592ffb21SWarner Losh * First, find the plane, crtc, and fb objects. If not available, 1753592ffb21SWarner Losh * we don't bother to call the driver. 1754592ffb21SWarner Losh */ 1755592ffb21SWarner Losh obj = drm_mode_object_find(dev, plane_req->plane_id, 1756592ffb21SWarner Losh DRM_MODE_OBJECT_PLANE); 1757592ffb21SWarner Losh if (!obj) { 1758592ffb21SWarner Losh DRM_DEBUG_KMS("Unknown plane ID %d\n", 1759592ffb21SWarner Losh plane_req->plane_id); 1760592ffb21SWarner Losh ret = -ENOENT; 1761592ffb21SWarner Losh goto out; 1762592ffb21SWarner Losh } 1763592ffb21SWarner Losh plane = obj_to_plane(obj); 1764592ffb21SWarner Losh 1765592ffb21SWarner Losh /* No fb means shut it down */ 1766592ffb21SWarner Losh if (!plane_req->fb_id) { 1767592ffb21SWarner Losh plane->funcs->disable_plane(plane); 1768592ffb21SWarner Losh plane->crtc = NULL; 1769592ffb21SWarner Losh plane->fb = NULL; 1770592ffb21SWarner Losh goto out; 1771592ffb21SWarner Losh } 1772592ffb21SWarner Losh 1773592ffb21SWarner Losh obj = drm_mode_object_find(dev, plane_req->crtc_id, 1774592ffb21SWarner Losh DRM_MODE_OBJECT_CRTC); 1775592ffb21SWarner Losh if (!obj) { 1776592ffb21SWarner Losh DRM_DEBUG_KMS("Unknown crtc ID %d\n", 1777592ffb21SWarner Losh plane_req->crtc_id); 1778592ffb21SWarner Losh ret = -ENOENT; 1779592ffb21SWarner Losh goto out; 1780592ffb21SWarner Losh } 1781592ffb21SWarner Losh crtc = obj_to_crtc(obj); 1782592ffb21SWarner Losh 1783592ffb21SWarner Losh obj = drm_mode_object_find(dev, plane_req->fb_id, 1784592ffb21SWarner Losh DRM_MODE_OBJECT_FB); 1785592ffb21SWarner Losh if (!obj) { 1786592ffb21SWarner Losh DRM_DEBUG_KMS("Unknown framebuffer ID %d\n", 1787592ffb21SWarner Losh plane_req->fb_id); 1788592ffb21SWarner Losh ret = -ENOENT; 1789592ffb21SWarner Losh goto out; 1790592ffb21SWarner Losh } 1791592ffb21SWarner Losh fb = obj_to_fb(obj); 1792592ffb21SWarner Losh 1793592ffb21SWarner Losh /* Check whether this plane supports the fb pixel format. */ 1794592ffb21SWarner Losh for (i = 0; i < plane->format_count; i++) 1795592ffb21SWarner Losh if (fb->pixel_format == plane->format_types[i]) 1796592ffb21SWarner Losh break; 1797592ffb21SWarner Losh if (i == plane->format_count) { 1798592ffb21SWarner Losh DRM_DEBUG_KMS("Invalid pixel format 0x%08x\n", fb->pixel_format); 1799592ffb21SWarner Losh ret = -EINVAL; 1800592ffb21SWarner Losh goto out; 1801592ffb21SWarner Losh } 1802592ffb21SWarner Losh 1803592ffb21SWarner Losh fb_width = fb->width << 16; 1804592ffb21SWarner Losh fb_height = fb->height << 16; 1805592ffb21SWarner Losh 1806592ffb21SWarner Losh /* Make sure source coordinates are inside the fb. */ 1807592ffb21SWarner Losh if (plane_req->src_w > fb_width || 1808592ffb21SWarner Losh plane_req->src_x > fb_width - plane_req->src_w || 1809592ffb21SWarner Losh plane_req->src_h > fb_height || 1810592ffb21SWarner Losh plane_req->src_y > fb_height - plane_req->src_h) { 1811592ffb21SWarner Losh DRM_DEBUG_KMS("Invalid source coordinates " 1812592ffb21SWarner Losh "%u.%06ux%u.%06u+%u.%06u+%u.%06u\n", 1813592ffb21SWarner Losh plane_req->src_w >> 16, 1814592ffb21SWarner Losh ((plane_req->src_w & 0xffff) * 15625) >> 10, 1815592ffb21SWarner Losh plane_req->src_h >> 16, 1816592ffb21SWarner Losh ((plane_req->src_h & 0xffff) * 15625) >> 10, 1817592ffb21SWarner Losh plane_req->src_x >> 16, 1818592ffb21SWarner Losh ((plane_req->src_x & 0xffff) * 15625) >> 10, 1819592ffb21SWarner Losh plane_req->src_y >> 16, 1820592ffb21SWarner Losh ((plane_req->src_y & 0xffff) * 15625) >> 10); 1821592ffb21SWarner Losh ret = -ENOSPC; 1822592ffb21SWarner Losh goto out; 1823592ffb21SWarner Losh } 1824592ffb21SWarner Losh 1825592ffb21SWarner Losh /* Give drivers some help against integer overflows */ 1826592ffb21SWarner Losh if (plane_req->crtc_w > INT_MAX || 1827592ffb21SWarner Losh plane_req->crtc_x > INT_MAX - (int32_t) plane_req->crtc_w || 1828592ffb21SWarner Losh plane_req->crtc_h > INT_MAX || 1829592ffb21SWarner Losh plane_req->crtc_y > INT_MAX - (int32_t) plane_req->crtc_h) { 1830592ffb21SWarner Losh DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n", 1831592ffb21SWarner Losh plane_req->crtc_w, plane_req->crtc_h, 1832592ffb21SWarner Losh plane_req->crtc_x, plane_req->crtc_y); 1833592ffb21SWarner Losh ret = -ERANGE; 1834592ffb21SWarner Losh goto out; 1835592ffb21SWarner Losh } 1836592ffb21SWarner Losh 1837592ffb21SWarner Losh ret = plane->funcs->update_plane(plane, crtc, fb, 1838592ffb21SWarner Losh plane_req->crtc_x, plane_req->crtc_y, 1839592ffb21SWarner Losh plane_req->crtc_w, plane_req->crtc_h, 1840592ffb21SWarner Losh plane_req->src_x, plane_req->src_y, 1841592ffb21SWarner Losh plane_req->src_w, plane_req->src_h); 1842592ffb21SWarner Losh if (!ret) { 1843592ffb21SWarner Losh plane->crtc = crtc; 1844592ffb21SWarner Losh plane->fb = fb; 1845592ffb21SWarner Losh } 1846592ffb21SWarner Losh 1847592ffb21SWarner Losh out: 1848592ffb21SWarner Losh sx_xunlock(&dev->mode_config.mutex); 1849592ffb21SWarner Losh 1850592ffb21SWarner Losh return ret; 1851592ffb21SWarner Losh } 1852592ffb21SWarner Losh 1853592ffb21SWarner Losh /** 1854592ffb21SWarner Losh * drm_mode_setcrtc - set CRTC configuration 1855592ffb21SWarner Losh * @inode: inode from the ioctl 1856592ffb21SWarner Losh * @filp: file * from the ioctl 1857592ffb21SWarner Losh * @cmd: cmd from ioctl 1858592ffb21SWarner Losh * @arg: arg from ioctl 1859592ffb21SWarner Losh * 1860592ffb21SWarner Losh * LOCKING: 1861592ffb21SWarner Losh * Takes mode config lock. 1862592ffb21SWarner Losh * 1863592ffb21SWarner Losh * Build a new CRTC configuration based on user request. 1864592ffb21SWarner Losh * 1865592ffb21SWarner Losh * Called by the user via ioctl. 1866592ffb21SWarner Losh * 1867592ffb21SWarner Losh * RETURNS: 1868592ffb21SWarner Losh * Zero on success, errno on failure. 1869592ffb21SWarner Losh */ 1870592ffb21SWarner Losh int drm_mode_setcrtc(struct drm_device *dev, void *data, 1871592ffb21SWarner Losh struct drm_file *file_priv) 1872592ffb21SWarner Losh { 1873592ffb21SWarner Losh struct drm_mode_config *config = &dev->mode_config; 1874592ffb21SWarner Losh struct drm_mode_crtc *crtc_req = data; 1875592ffb21SWarner Losh struct drm_mode_object *obj; 1876592ffb21SWarner Losh struct drm_crtc *crtc; 1877592ffb21SWarner Losh struct drm_connector **connector_set = NULL, *connector; 1878592ffb21SWarner Losh struct drm_framebuffer *fb = NULL; 1879592ffb21SWarner Losh struct drm_display_mode *mode = NULL; 1880592ffb21SWarner Losh struct drm_mode_set set; 1881592ffb21SWarner Losh uint32_t __user *set_connectors_ptr; 1882592ffb21SWarner Losh int ret; 1883592ffb21SWarner Losh int i; 1884592ffb21SWarner Losh 1885592ffb21SWarner Losh if (!drm_core_check_feature(dev, DRIVER_MODESET)) 1886592ffb21SWarner Losh return -EINVAL; 1887592ffb21SWarner Losh 1888592ffb21SWarner Losh /* For some reason crtc x/y offsets are signed internally. */ 1889592ffb21SWarner Losh if (crtc_req->x > INT_MAX || crtc_req->y > INT_MAX) 1890592ffb21SWarner Losh return -ERANGE; 1891592ffb21SWarner Losh 1892592ffb21SWarner Losh sx_xlock(&dev->mode_config.mutex); 1893592ffb21SWarner Losh obj = drm_mode_object_find(dev, crtc_req->crtc_id, 1894592ffb21SWarner Losh DRM_MODE_OBJECT_CRTC); 1895592ffb21SWarner Losh if (!obj) { 1896592ffb21SWarner Losh DRM_DEBUG_KMS("Unknown CRTC ID %d\n", crtc_req->crtc_id); 1897592ffb21SWarner Losh ret = -EINVAL; 1898592ffb21SWarner Losh goto out; 1899592ffb21SWarner Losh } 1900592ffb21SWarner Losh crtc = obj_to_crtc(obj); 1901592ffb21SWarner Losh DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id); 1902592ffb21SWarner Losh 1903592ffb21SWarner Losh if (crtc_req->mode_valid) { 1904592ffb21SWarner Losh int hdisplay, vdisplay; 1905592ffb21SWarner Losh /* If we have a mode we need a framebuffer. */ 1906592ffb21SWarner Losh /* If we pass -1, set the mode with the currently bound fb */ 1907592ffb21SWarner Losh if (crtc_req->fb_id == -1) { 1908592ffb21SWarner Losh if (!crtc->fb) { 1909592ffb21SWarner Losh DRM_DEBUG_KMS("CRTC doesn't have current FB\n"); 1910592ffb21SWarner Losh ret = -EINVAL; 1911592ffb21SWarner Losh goto out; 1912592ffb21SWarner Losh } 1913592ffb21SWarner Losh fb = crtc->fb; 1914592ffb21SWarner Losh } else { 1915592ffb21SWarner Losh obj = drm_mode_object_find(dev, crtc_req->fb_id, 1916592ffb21SWarner Losh DRM_MODE_OBJECT_FB); 1917592ffb21SWarner Losh if (!obj) { 1918592ffb21SWarner Losh DRM_DEBUG_KMS("Unknown FB ID%d\n", 1919592ffb21SWarner Losh crtc_req->fb_id); 1920592ffb21SWarner Losh ret = -EINVAL; 1921592ffb21SWarner Losh goto out; 1922592ffb21SWarner Losh } 1923592ffb21SWarner Losh fb = obj_to_fb(obj); 1924592ffb21SWarner Losh } 1925592ffb21SWarner Losh 1926592ffb21SWarner Losh mode = drm_mode_create(dev); 1927592ffb21SWarner Losh if (!mode) { 1928592ffb21SWarner Losh ret = -ENOMEM; 1929592ffb21SWarner Losh goto out; 1930592ffb21SWarner Losh } 1931592ffb21SWarner Losh 1932592ffb21SWarner Losh ret = drm_crtc_convert_umode(mode, &crtc_req->mode); 1933592ffb21SWarner Losh if (ret) { 1934592ffb21SWarner Losh DRM_DEBUG_KMS("Invalid mode\n"); 1935592ffb21SWarner Losh goto out; 1936592ffb21SWarner Losh } 1937592ffb21SWarner Losh 1938592ffb21SWarner Losh drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); 1939592ffb21SWarner Losh 1940592ffb21SWarner Losh hdisplay = mode->hdisplay; 1941592ffb21SWarner Losh vdisplay = mode->vdisplay; 1942592ffb21SWarner Losh 1943592ffb21SWarner Losh if (crtc->invert_dimensions) { 1944592ffb21SWarner Losh int tmp; 1945592ffb21SWarner Losh tmp = vdisplay; 1946592ffb21SWarner Losh vdisplay = hdisplay; 1947592ffb21SWarner Losh hdisplay = tmp; 1948592ffb21SWarner Losh } 1949592ffb21SWarner Losh 1950592ffb21SWarner Losh if (hdisplay > fb->width || 1951592ffb21SWarner Losh vdisplay > fb->height || 1952592ffb21SWarner Losh crtc_req->x > fb->width - hdisplay || 1953592ffb21SWarner Losh crtc_req->y > fb->height - vdisplay) { 1954592ffb21SWarner Losh DRM_DEBUG_KMS("Invalid fb size %ux%u for CRTC viewport %ux%u+%d+%d%s.\n", 1955592ffb21SWarner Losh fb->width, fb->height, 1956592ffb21SWarner Losh hdisplay, vdisplay, crtc_req->x, crtc_req->y, 1957592ffb21SWarner Losh crtc->invert_dimensions ? " (inverted)" : ""); 1958592ffb21SWarner Losh ret = -ENOSPC; 1959592ffb21SWarner Losh goto out; 1960592ffb21SWarner Losh } 1961592ffb21SWarner Losh } 1962592ffb21SWarner Losh 1963592ffb21SWarner Losh if (crtc_req->count_connectors == 0 && mode) { 1964592ffb21SWarner Losh DRM_DEBUG_KMS("Count connectors is 0 but mode set\n"); 1965592ffb21SWarner Losh ret = -EINVAL; 1966592ffb21SWarner Losh goto out; 1967592ffb21SWarner Losh } 1968592ffb21SWarner Losh 1969592ffb21SWarner Losh if (crtc_req->count_connectors > 0 && (!mode || !fb)) { 1970592ffb21SWarner Losh DRM_DEBUG_KMS("Count connectors is %d but no mode or fb set\n", 1971592ffb21SWarner Losh crtc_req->count_connectors); 1972592ffb21SWarner Losh ret = -EINVAL; 1973592ffb21SWarner Losh goto out; 1974592ffb21SWarner Losh } 1975592ffb21SWarner Losh 1976592ffb21SWarner Losh if (crtc_req->count_connectors > 0) { 1977592ffb21SWarner Losh u32 out_id; 1978592ffb21SWarner Losh 1979592ffb21SWarner Losh /* Avoid unbounded kernel memory allocation */ 1980592ffb21SWarner Losh if (crtc_req->count_connectors > config->num_connector) { 1981592ffb21SWarner Losh ret = -EINVAL; 1982592ffb21SWarner Losh goto out; 1983592ffb21SWarner Losh } 1984592ffb21SWarner Losh 1985592ffb21SWarner Losh connector_set = malloc(crtc_req->count_connectors * 1986592ffb21SWarner Losh sizeof(struct drm_connector *), 1987592ffb21SWarner Losh DRM_MEM_KMS, M_WAITOK); 1988592ffb21SWarner Losh 1989592ffb21SWarner Losh for (i = 0; i < crtc_req->count_connectors; i++) { 1990592ffb21SWarner Losh set_connectors_ptr = (uint32_t __user *)(unsigned long)crtc_req->set_connectors_ptr; 1991592ffb21SWarner Losh if (get_user(out_id, &set_connectors_ptr[i])) { 1992592ffb21SWarner Losh ret = -EFAULT; 1993592ffb21SWarner Losh goto out; 1994592ffb21SWarner Losh } 1995592ffb21SWarner Losh 1996592ffb21SWarner Losh obj = drm_mode_object_find(dev, out_id, 1997592ffb21SWarner Losh DRM_MODE_OBJECT_CONNECTOR); 1998592ffb21SWarner Losh if (!obj) { 1999592ffb21SWarner Losh DRM_DEBUG_KMS("Connector id %d unknown\n", 2000592ffb21SWarner Losh out_id); 2001592ffb21SWarner Losh ret = -EINVAL; 2002592ffb21SWarner Losh goto out; 2003592ffb21SWarner Losh } 2004592ffb21SWarner Losh connector = obj_to_connector(obj); 2005592ffb21SWarner Losh DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", 2006592ffb21SWarner Losh connector->base.id, 2007592ffb21SWarner Losh drm_get_connector_name(connector)); 2008592ffb21SWarner Losh 2009592ffb21SWarner Losh connector_set[i] = connector; 2010592ffb21SWarner Losh } 2011592ffb21SWarner Losh } 2012592ffb21SWarner Losh 2013592ffb21SWarner Losh set.crtc = crtc; 2014592ffb21SWarner Losh set.x = crtc_req->x; 2015592ffb21SWarner Losh set.y = crtc_req->y; 2016592ffb21SWarner Losh set.mode = mode; 2017592ffb21SWarner Losh set.connectors = connector_set; 2018592ffb21SWarner Losh set.num_connectors = crtc_req->count_connectors; 2019592ffb21SWarner Losh set.fb = fb; 2020592ffb21SWarner Losh ret = crtc->funcs->set_config(&set); 2021592ffb21SWarner Losh 2022592ffb21SWarner Losh out: 2023592ffb21SWarner Losh free(connector_set, DRM_MEM_KMS); 2024592ffb21SWarner Losh drm_mode_destroy(dev, mode); 2025592ffb21SWarner Losh sx_xunlock(&dev->mode_config.mutex); 2026592ffb21SWarner Losh return ret; 2027592ffb21SWarner Losh } 2028592ffb21SWarner Losh 2029592ffb21SWarner Losh int drm_mode_cursor_ioctl(struct drm_device *dev, 2030592ffb21SWarner Losh void *data, struct drm_file *file_priv) 2031592ffb21SWarner Losh { 2032592ffb21SWarner Losh struct drm_mode_cursor *req = data; 2033592ffb21SWarner Losh struct drm_mode_object *obj; 2034592ffb21SWarner Losh struct drm_crtc *crtc; 2035592ffb21SWarner Losh int ret = 0; 2036592ffb21SWarner Losh 2037592ffb21SWarner Losh if (!drm_core_check_feature(dev, DRIVER_MODESET)) 2038592ffb21SWarner Losh return -EINVAL; 2039592ffb21SWarner Losh 2040592ffb21SWarner Losh if (!req->flags || (~DRM_MODE_CURSOR_FLAGS & req->flags)) 2041592ffb21SWarner Losh return -EINVAL; 2042592ffb21SWarner Losh 2043592ffb21SWarner Losh sx_xlock(&dev->mode_config.mutex); 2044592ffb21SWarner Losh obj = drm_mode_object_find(dev, req->crtc_id, DRM_MODE_OBJECT_CRTC); 2045592ffb21SWarner Losh if (!obj) { 2046592ffb21SWarner Losh DRM_DEBUG_KMS("Unknown CRTC ID %d\n", req->crtc_id); 2047592ffb21SWarner Losh ret = -EINVAL; 2048592ffb21SWarner Losh goto out; 2049592ffb21SWarner Losh } 2050592ffb21SWarner Losh crtc = obj_to_crtc(obj); 2051592ffb21SWarner Losh 2052592ffb21SWarner Losh if (req->flags & DRM_MODE_CURSOR_BO) { 2053592ffb21SWarner Losh if (!crtc->funcs->cursor_set) { 2054592ffb21SWarner Losh ret = -ENXIO; 2055592ffb21SWarner Losh goto out; 2056592ffb21SWarner Losh } 2057592ffb21SWarner Losh /* Turns off the cursor if handle is 0 */ 2058592ffb21SWarner Losh ret = crtc->funcs->cursor_set(crtc, file_priv, req->handle, 2059592ffb21SWarner Losh req->width, req->height); 2060592ffb21SWarner Losh } 2061592ffb21SWarner Losh 2062592ffb21SWarner Losh if (req->flags & DRM_MODE_CURSOR_MOVE) { 2063592ffb21SWarner Losh if (crtc->funcs->cursor_move) { 2064592ffb21SWarner Losh ret = crtc->funcs->cursor_move(crtc, req->x, req->y); 2065592ffb21SWarner Losh } else { 2066592ffb21SWarner Losh ret = -EFAULT; 2067592ffb21SWarner Losh goto out; 2068592ffb21SWarner Losh } 2069592ffb21SWarner Losh } 2070592ffb21SWarner Losh out: 2071592ffb21SWarner Losh sx_xunlock(&dev->mode_config.mutex); 2072592ffb21SWarner Losh return ret; 2073592ffb21SWarner Losh } 2074592ffb21SWarner Losh 2075592ffb21SWarner Losh /* Original addfb only supported RGB formats, so figure out which one */ 2076592ffb21SWarner Losh uint32_t drm_mode_legacy_fb_format(uint32_t bpp, uint32_t depth) 2077592ffb21SWarner Losh { 2078592ffb21SWarner Losh uint32_t fmt; 2079592ffb21SWarner Losh 2080592ffb21SWarner Losh switch (bpp) { 2081592ffb21SWarner Losh case 8: 2082592ffb21SWarner Losh fmt = DRM_FORMAT_C8; 2083592ffb21SWarner Losh break; 2084592ffb21SWarner Losh case 16: 2085592ffb21SWarner Losh if (depth == 15) 2086592ffb21SWarner Losh fmt = DRM_FORMAT_XRGB1555; 2087592ffb21SWarner Losh else 2088592ffb21SWarner Losh fmt = DRM_FORMAT_RGB565; 2089592ffb21SWarner Losh break; 2090592ffb21SWarner Losh case 24: 2091592ffb21SWarner Losh fmt = DRM_FORMAT_RGB888; 2092592ffb21SWarner Losh break; 2093592ffb21SWarner Losh case 32: 2094592ffb21SWarner Losh if (depth == 24) 2095592ffb21SWarner Losh fmt = DRM_FORMAT_XRGB8888; 2096592ffb21SWarner Losh else if (depth == 30) 2097592ffb21SWarner Losh fmt = DRM_FORMAT_XRGB2101010; 2098592ffb21SWarner Losh else 2099592ffb21SWarner Losh fmt = DRM_FORMAT_ARGB8888; 2100592ffb21SWarner Losh break; 2101592ffb21SWarner Losh default: 2102592ffb21SWarner Losh DRM_ERROR("bad bpp, assuming x8r8g8b8 pixel format\n"); 2103592ffb21SWarner Losh fmt = DRM_FORMAT_XRGB8888; 2104592ffb21SWarner Losh break; 2105592ffb21SWarner Losh } 2106592ffb21SWarner Losh 2107592ffb21SWarner Losh return fmt; 2108592ffb21SWarner Losh } 2109592ffb21SWarner Losh EXPORT_SYMBOL(drm_mode_legacy_fb_format); 2110592ffb21SWarner Losh 2111592ffb21SWarner Losh /** 2112592ffb21SWarner Losh * drm_mode_addfb - add an FB to the graphics configuration 2113592ffb21SWarner Losh * @inode: inode from the ioctl 2114592ffb21SWarner Losh * @filp: file * from the ioctl 2115592ffb21SWarner Losh * @cmd: cmd from ioctl 2116592ffb21SWarner Losh * @arg: arg from ioctl 2117592ffb21SWarner Losh * 2118592ffb21SWarner Losh * LOCKING: 2119592ffb21SWarner Losh * Takes mode config lock. 2120592ffb21SWarner Losh * 2121592ffb21SWarner Losh * Add a new FB to the specified CRTC, given a user request. 2122592ffb21SWarner Losh * 2123592ffb21SWarner Losh * Called by the user via ioctl. 2124592ffb21SWarner Losh * 2125592ffb21SWarner Losh * RETURNS: 2126592ffb21SWarner Losh * Zero on success, errno on failure. 2127592ffb21SWarner Losh */ 2128592ffb21SWarner Losh int drm_mode_addfb(struct drm_device *dev, 2129592ffb21SWarner Losh void *data, struct drm_file *file_priv) 2130592ffb21SWarner Losh { 2131592ffb21SWarner Losh struct drm_mode_fb_cmd *or = data; 2132592ffb21SWarner Losh struct drm_mode_fb_cmd2 r = {}; 2133592ffb21SWarner Losh struct drm_mode_config *config = &dev->mode_config; 2134592ffb21SWarner Losh struct drm_framebuffer *fb; 2135592ffb21SWarner Losh int ret = 0; 2136592ffb21SWarner Losh 2137592ffb21SWarner Losh /* Use new struct with format internally */ 2138592ffb21SWarner Losh r.fb_id = or->fb_id; 2139592ffb21SWarner Losh r.width = or->width; 2140592ffb21SWarner Losh r.height = or->height; 2141592ffb21SWarner Losh r.pitches[0] = or->pitch; 2142592ffb21SWarner Losh r.pixel_format = drm_mode_legacy_fb_format(or->bpp, or->depth); 2143592ffb21SWarner Losh r.handles[0] = or->handle; 2144592ffb21SWarner Losh 2145592ffb21SWarner Losh if (!drm_core_check_feature(dev, DRIVER_MODESET)) 2146592ffb21SWarner Losh return -EINVAL; 2147592ffb21SWarner Losh 2148592ffb21SWarner Losh if ((config->min_width > r.width) || (r.width > config->max_width)) 2149592ffb21SWarner Losh return -EINVAL; 2150592ffb21SWarner Losh 2151592ffb21SWarner Losh if ((config->min_height > r.height) || (r.height > config->max_height)) 2152592ffb21SWarner Losh return -EINVAL; 2153592ffb21SWarner Losh 2154592ffb21SWarner Losh sx_xlock(&dev->mode_config.mutex); 2155592ffb21SWarner Losh 2156592ffb21SWarner Losh /* TODO check buffer is sufficiently large */ 2157592ffb21SWarner Losh /* TODO setup destructor callback */ 2158592ffb21SWarner Losh 2159592ffb21SWarner Losh ret = dev->mode_config.funcs->fb_create(dev, file_priv, &r, &fb); 2160592ffb21SWarner Losh if (ret != 0) { 2161592ffb21SWarner Losh DRM_DEBUG_KMS("could not create framebuffer\n"); 2162592ffb21SWarner Losh goto out; 2163592ffb21SWarner Losh } 2164592ffb21SWarner Losh 2165592ffb21SWarner Losh or->fb_id = fb->base.id; 2166592ffb21SWarner Losh list_add(&fb->filp_head, &file_priv->fbs); 2167592ffb21SWarner Losh DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id); 2168592ffb21SWarner Losh 2169592ffb21SWarner Losh out: 2170592ffb21SWarner Losh sx_xunlock(&dev->mode_config.mutex); 2171592ffb21SWarner Losh return ret; 2172592ffb21SWarner Losh } 2173592ffb21SWarner Losh 2174592ffb21SWarner Losh static int format_check(const struct drm_mode_fb_cmd2 *r) 2175592ffb21SWarner Losh { 2176592ffb21SWarner Losh uint32_t format = r->pixel_format & ~DRM_FORMAT_BIG_ENDIAN; 2177592ffb21SWarner Losh 2178592ffb21SWarner Losh switch (format) { 2179592ffb21SWarner Losh case DRM_FORMAT_C8: 2180592ffb21SWarner Losh case DRM_FORMAT_RGB332: 2181592ffb21SWarner Losh case DRM_FORMAT_BGR233: 2182592ffb21SWarner Losh case DRM_FORMAT_XRGB4444: 2183592ffb21SWarner Losh case DRM_FORMAT_XBGR4444: 2184592ffb21SWarner Losh case DRM_FORMAT_RGBX4444: 2185592ffb21SWarner Losh case DRM_FORMAT_BGRX4444: 2186592ffb21SWarner Losh case DRM_FORMAT_ARGB4444: 2187592ffb21SWarner Losh case DRM_FORMAT_ABGR4444: 2188592ffb21SWarner Losh case DRM_FORMAT_RGBA4444: 2189592ffb21SWarner Losh case DRM_FORMAT_BGRA4444: 2190592ffb21SWarner Losh case DRM_FORMAT_XRGB1555: 2191592ffb21SWarner Losh case DRM_FORMAT_XBGR1555: 2192592ffb21SWarner Losh case DRM_FORMAT_RGBX5551: 2193592ffb21SWarner Losh case DRM_FORMAT_BGRX5551: 2194592ffb21SWarner Losh case DRM_FORMAT_ARGB1555: 2195592ffb21SWarner Losh case DRM_FORMAT_ABGR1555: 2196592ffb21SWarner Losh case DRM_FORMAT_RGBA5551: 2197592ffb21SWarner Losh case DRM_FORMAT_BGRA5551: 2198592ffb21SWarner Losh case DRM_FORMAT_RGB565: 2199592ffb21SWarner Losh case DRM_FORMAT_BGR565: 2200592ffb21SWarner Losh case DRM_FORMAT_RGB888: 2201592ffb21SWarner Losh case DRM_FORMAT_BGR888: 2202592ffb21SWarner Losh case DRM_FORMAT_XRGB8888: 2203592ffb21SWarner Losh case DRM_FORMAT_XBGR8888: 2204592ffb21SWarner Losh case DRM_FORMAT_RGBX8888: 2205592ffb21SWarner Losh case DRM_FORMAT_BGRX8888: 2206592ffb21SWarner Losh case DRM_FORMAT_ARGB8888: 2207592ffb21SWarner Losh case DRM_FORMAT_ABGR8888: 2208592ffb21SWarner Losh case DRM_FORMAT_RGBA8888: 2209592ffb21SWarner Losh case DRM_FORMAT_BGRA8888: 2210592ffb21SWarner Losh case DRM_FORMAT_XRGB2101010: 2211592ffb21SWarner Losh case DRM_FORMAT_XBGR2101010: 2212592ffb21SWarner Losh case DRM_FORMAT_RGBX1010102: 2213592ffb21SWarner Losh case DRM_FORMAT_BGRX1010102: 2214592ffb21SWarner Losh case DRM_FORMAT_ARGB2101010: 2215592ffb21SWarner Losh case DRM_FORMAT_ABGR2101010: 2216592ffb21SWarner Losh case DRM_FORMAT_RGBA1010102: 2217592ffb21SWarner Losh case DRM_FORMAT_BGRA1010102: 2218592ffb21SWarner Losh case DRM_FORMAT_YUYV: 2219592ffb21SWarner Losh case DRM_FORMAT_YVYU: 2220592ffb21SWarner Losh case DRM_FORMAT_UYVY: 2221592ffb21SWarner Losh case DRM_FORMAT_VYUY: 2222592ffb21SWarner Losh case DRM_FORMAT_AYUV: 2223592ffb21SWarner Losh case DRM_FORMAT_NV12: 2224592ffb21SWarner Losh case DRM_FORMAT_NV21: 2225592ffb21SWarner Losh case DRM_FORMAT_NV16: 2226592ffb21SWarner Losh case DRM_FORMAT_NV61: 2227592ffb21SWarner Losh case DRM_FORMAT_NV24: 2228592ffb21SWarner Losh case DRM_FORMAT_NV42: 2229592ffb21SWarner Losh case DRM_FORMAT_YUV410: 2230592ffb21SWarner Losh case DRM_FORMAT_YVU410: 2231592ffb21SWarner Losh case DRM_FORMAT_YUV411: 2232592ffb21SWarner Losh case DRM_FORMAT_YVU411: 2233592ffb21SWarner Losh case DRM_FORMAT_YUV420: 2234592ffb21SWarner Losh case DRM_FORMAT_YVU420: 2235592ffb21SWarner Losh case DRM_FORMAT_YUV422: 2236592ffb21SWarner Losh case DRM_FORMAT_YVU422: 2237592ffb21SWarner Losh case DRM_FORMAT_YUV444: 2238592ffb21SWarner Losh case DRM_FORMAT_YVU444: 2239592ffb21SWarner Losh return 0; 2240592ffb21SWarner Losh default: 2241592ffb21SWarner Losh return -EINVAL; 2242592ffb21SWarner Losh } 2243592ffb21SWarner Losh } 2244592ffb21SWarner Losh 2245592ffb21SWarner Losh static int framebuffer_check(const struct drm_mode_fb_cmd2 *r) 2246592ffb21SWarner Losh { 2247592ffb21SWarner Losh int ret, hsub, vsub, num_planes, i; 2248592ffb21SWarner Losh 2249592ffb21SWarner Losh ret = format_check(r); 2250592ffb21SWarner Losh if (ret) { 2251592ffb21SWarner Losh DRM_DEBUG_KMS("bad framebuffer format 0x%08x\n", r->pixel_format); 2252592ffb21SWarner Losh return ret; 2253592ffb21SWarner Losh } 2254592ffb21SWarner Losh 2255592ffb21SWarner Losh hsub = drm_format_horz_chroma_subsampling(r->pixel_format); 2256592ffb21SWarner Losh vsub = drm_format_vert_chroma_subsampling(r->pixel_format); 2257592ffb21SWarner Losh num_planes = drm_format_num_planes(r->pixel_format); 2258592ffb21SWarner Losh 2259592ffb21SWarner Losh if (r->width == 0 || r->width % hsub) { 2260592ffb21SWarner Losh DRM_DEBUG_KMS("bad framebuffer width %u\n", r->height); 2261592ffb21SWarner Losh return -EINVAL; 2262592ffb21SWarner Losh } 2263592ffb21SWarner Losh 2264592ffb21SWarner Losh if (r->height == 0 || r->height % vsub) { 2265592ffb21SWarner Losh DRM_DEBUG_KMS("bad framebuffer height %u\n", r->height); 2266592ffb21SWarner Losh return -EINVAL; 2267592ffb21SWarner Losh } 2268592ffb21SWarner Losh 2269592ffb21SWarner Losh for (i = 0; i < num_planes; i++) { 2270592ffb21SWarner Losh unsigned int width = r->width / (i != 0 ? hsub : 1); 2271592ffb21SWarner Losh unsigned int height = r->height / (i != 0 ? vsub : 1); 2272592ffb21SWarner Losh unsigned int cpp = drm_format_plane_cpp(r->pixel_format, i); 2273592ffb21SWarner Losh 2274592ffb21SWarner Losh if (!r->handles[i]) { 2275592ffb21SWarner Losh DRM_DEBUG_KMS("no buffer object handle for plane %d\n", i); 2276592ffb21SWarner Losh return -EINVAL; 2277592ffb21SWarner Losh } 2278592ffb21SWarner Losh 2279592ffb21SWarner Losh if ((uint64_t) width * cpp > UINT_MAX) 2280592ffb21SWarner Losh return -ERANGE; 2281592ffb21SWarner Losh 2282592ffb21SWarner Losh if ((uint64_t) height * r->pitches[i] + r->offsets[i] > UINT_MAX) 2283592ffb21SWarner Losh return -ERANGE; 2284592ffb21SWarner Losh 2285592ffb21SWarner Losh if (r->pitches[i] < width * cpp) { 2286592ffb21SWarner Losh DRM_DEBUG_KMS("bad pitch %u for plane %d\n", r->pitches[i], i); 2287592ffb21SWarner Losh return -EINVAL; 2288592ffb21SWarner Losh } 2289592ffb21SWarner Losh } 2290592ffb21SWarner Losh 2291592ffb21SWarner Losh return 0; 2292592ffb21SWarner Losh } 2293592ffb21SWarner Losh 2294592ffb21SWarner Losh /** 2295592ffb21SWarner Losh * drm_mode_addfb2 - add an FB to the graphics configuration 2296592ffb21SWarner Losh * @inode: inode from the ioctl 2297592ffb21SWarner Losh * @filp: file * from the ioctl 2298592ffb21SWarner Losh * @cmd: cmd from ioctl 2299592ffb21SWarner Losh * @arg: arg from ioctl 2300592ffb21SWarner Losh * 2301592ffb21SWarner Losh * LOCKING: 2302592ffb21SWarner Losh * Takes mode config lock. 2303592ffb21SWarner Losh * 2304592ffb21SWarner Losh * Add a new FB to the specified CRTC, given a user request with format. 2305592ffb21SWarner Losh * 2306592ffb21SWarner Losh * Called by the user via ioctl. 2307592ffb21SWarner Losh * 2308592ffb21SWarner Losh * RETURNS: 2309592ffb21SWarner Losh * Zero on success, errno on failure. 2310592ffb21SWarner Losh */ 2311592ffb21SWarner Losh int drm_mode_addfb2(struct drm_device *dev, 2312592ffb21SWarner Losh void *data, struct drm_file *file_priv) 2313592ffb21SWarner Losh { 2314592ffb21SWarner Losh struct drm_mode_fb_cmd2 *r = data; 2315592ffb21SWarner Losh struct drm_mode_config *config = &dev->mode_config; 2316592ffb21SWarner Losh struct drm_framebuffer *fb; 2317592ffb21SWarner Losh int ret; 2318592ffb21SWarner Losh 2319592ffb21SWarner Losh if (!drm_core_check_feature(dev, DRIVER_MODESET)) 2320592ffb21SWarner Losh return -EINVAL; 2321592ffb21SWarner Losh 2322592ffb21SWarner Losh if (r->flags & ~DRM_MODE_FB_INTERLACED) { 2323592ffb21SWarner Losh DRM_DEBUG_KMS("bad framebuffer flags 0x%08x\n", r->flags); 2324592ffb21SWarner Losh return -EINVAL; 2325592ffb21SWarner Losh } 2326592ffb21SWarner Losh 2327592ffb21SWarner Losh if ((config->min_width > r->width) || (r->width > config->max_width)) { 2328592ffb21SWarner Losh DRM_DEBUG_KMS("bad framebuffer width %d, should be >= %d && <= %d\n", 2329592ffb21SWarner Losh r->width, config->min_width, config->max_width); 2330592ffb21SWarner Losh return -EINVAL; 2331592ffb21SWarner Losh } 2332592ffb21SWarner Losh if ((config->min_height > r->height) || (r->height > config->max_height)) { 2333592ffb21SWarner Losh DRM_DEBUG_KMS("bad framebuffer height %d, should be >= %d && <= %d\n", 2334592ffb21SWarner Losh r->height, config->min_height, config->max_height); 2335592ffb21SWarner Losh return -EINVAL; 2336592ffb21SWarner Losh } 2337592ffb21SWarner Losh 2338592ffb21SWarner Losh ret = framebuffer_check(r); 2339592ffb21SWarner Losh if (ret) 2340592ffb21SWarner Losh return ret; 2341592ffb21SWarner Losh 2342592ffb21SWarner Losh sx_xlock(&dev->mode_config.mutex); 2343592ffb21SWarner Losh 2344592ffb21SWarner Losh ret = dev->mode_config.funcs->fb_create(dev, file_priv, r, &fb); 2345592ffb21SWarner Losh if (ret != 0) { 2346592ffb21SWarner Losh DRM_DEBUG_KMS("could not create framebuffer\n"); 2347592ffb21SWarner Losh goto out; 2348592ffb21SWarner Losh } 2349592ffb21SWarner Losh 2350592ffb21SWarner Losh r->fb_id = fb->base.id; 2351592ffb21SWarner Losh list_add(&fb->filp_head, &file_priv->fbs); 2352592ffb21SWarner Losh DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id); 2353592ffb21SWarner Losh 2354592ffb21SWarner Losh out: 2355592ffb21SWarner Losh sx_xunlock(&dev->mode_config.mutex); 2356592ffb21SWarner Losh return ret; 2357592ffb21SWarner Losh } 2358592ffb21SWarner Losh 2359592ffb21SWarner Losh /** 2360592ffb21SWarner Losh * drm_mode_rmfb - remove an FB from the configuration 2361592ffb21SWarner Losh * @inode: inode from the ioctl 2362592ffb21SWarner Losh * @filp: file * from the ioctl 2363592ffb21SWarner Losh * @cmd: cmd from ioctl 2364592ffb21SWarner Losh * @arg: arg from ioctl 2365592ffb21SWarner Losh * 2366592ffb21SWarner Losh * LOCKING: 2367592ffb21SWarner Losh * Takes mode config lock. 2368592ffb21SWarner Losh * 2369592ffb21SWarner Losh * Remove the FB specified by the user. 2370592ffb21SWarner Losh * 2371592ffb21SWarner Losh * Called by the user via ioctl. 2372592ffb21SWarner Losh * 2373592ffb21SWarner Losh * RETURNS: 2374592ffb21SWarner Losh * Zero on success, errno on failure. 2375592ffb21SWarner Losh */ 2376592ffb21SWarner Losh int drm_mode_rmfb(struct drm_device *dev, 2377592ffb21SWarner Losh void *data, struct drm_file *file_priv) 2378592ffb21SWarner Losh { 2379592ffb21SWarner Losh struct drm_mode_object *obj; 2380592ffb21SWarner Losh struct drm_framebuffer *fb = NULL; 2381592ffb21SWarner Losh struct drm_framebuffer *fbl = NULL; 2382592ffb21SWarner Losh uint32_t *id = data; 2383592ffb21SWarner Losh int ret = 0; 2384592ffb21SWarner Losh int found = 0; 2385592ffb21SWarner Losh 2386592ffb21SWarner Losh if (!drm_core_check_feature(dev, DRIVER_MODESET)) 2387592ffb21SWarner Losh return -EINVAL; 2388592ffb21SWarner Losh 2389592ffb21SWarner Losh sx_xlock(&dev->mode_config.mutex); 2390592ffb21SWarner Losh obj = drm_mode_object_find(dev, *id, DRM_MODE_OBJECT_FB); 2391592ffb21SWarner Losh /* TODO check that we really get a framebuffer back. */ 2392592ffb21SWarner Losh if (!obj) { 2393592ffb21SWarner Losh ret = -EINVAL; 2394592ffb21SWarner Losh goto out; 2395592ffb21SWarner Losh } 2396592ffb21SWarner Losh fb = obj_to_fb(obj); 2397592ffb21SWarner Losh 2398592ffb21SWarner Losh list_for_each_entry(fbl, &file_priv->fbs, filp_head) 2399592ffb21SWarner Losh if (fb == fbl) 2400592ffb21SWarner Losh found = 1; 2401592ffb21SWarner Losh 2402592ffb21SWarner Losh if (!found) { 2403592ffb21SWarner Losh ret = -EINVAL; 2404592ffb21SWarner Losh goto out; 2405592ffb21SWarner Losh } 2406592ffb21SWarner Losh 2407592ffb21SWarner Losh drm_framebuffer_remove(fb); 2408592ffb21SWarner Losh 2409592ffb21SWarner Losh out: 2410592ffb21SWarner Losh sx_xunlock(&dev->mode_config.mutex); 2411592ffb21SWarner Losh return ret; 2412592ffb21SWarner Losh } 2413592ffb21SWarner Losh 2414592ffb21SWarner Losh /** 2415592ffb21SWarner Losh * drm_mode_getfb - get FB info 2416592ffb21SWarner Losh * @inode: inode from the ioctl 2417592ffb21SWarner Losh * @filp: file * from the ioctl 2418592ffb21SWarner Losh * @cmd: cmd from ioctl 2419592ffb21SWarner Losh * @arg: arg from ioctl 2420592ffb21SWarner Losh * 2421592ffb21SWarner Losh * LOCKING: 2422592ffb21SWarner Losh * Takes mode config lock. 2423592ffb21SWarner Losh * 2424592ffb21SWarner Losh * Lookup the FB given its ID and return info about it. 2425592ffb21SWarner Losh * 2426592ffb21SWarner Losh * Called by the user via ioctl. 2427592ffb21SWarner Losh * 2428592ffb21SWarner Losh * RETURNS: 2429592ffb21SWarner Losh * Zero on success, errno on failure. 2430592ffb21SWarner Losh */ 2431592ffb21SWarner Losh int drm_mode_getfb(struct drm_device *dev, 2432592ffb21SWarner Losh void *data, struct drm_file *file_priv) 2433592ffb21SWarner Losh { 2434592ffb21SWarner Losh struct drm_mode_fb_cmd *r = data; 2435592ffb21SWarner Losh struct drm_mode_object *obj; 2436592ffb21SWarner Losh struct drm_framebuffer *fb; 2437592ffb21SWarner Losh int ret = 0; 2438592ffb21SWarner Losh 2439592ffb21SWarner Losh if (!drm_core_check_feature(dev, DRIVER_MODESET)) 2440592ffb21SWarner Losh return -EINVAL; 2441592ffb21SWarner Losh 2442592ffb21SWarner Losh sx_xlock(&dev->mode_config.mutex); 2443592ffb21SWarner Losh obj = drm_mode_object_find(dev, r->fb_id, DRM_MODE_OBJECT_FB); 2444592ffb21SWarner Losh if (!obj) { 2445592ffb21SWarner Losh ret = -EINVAL; 2446592ffb21SWarner Losh goto out; 2447592ffb21SWarner Losh } 2448592ffb21SWarner Losh fb = obj_to_fb(obj); 2449592ffb21SWarner Losh 2450592ffb21SWarner Losh r->height = fb->height; 2451592ffb21SWarner Losh r->width = fb->width; 2452592ffb21SWarner Losh r->depth = fb->depth; 2453592ffb21SWarner Losh r->bpp = fb->bits_per_pixel; 2454592ffb21SWarner Losh r->pitch = fb->pitches[0]; 2455592ffb21SWarner Losh r->handle = 0; 2456592ffb21SWarner Losh fb->funcs->create_handle(fb, file_priv, &r->handle); 2457592ffb21SWarner Losh 2458592ffb21SWarner Losh out: 2459592ffb21SWarner Losh sx_xunlock(&dev->mode_config.mutex); 2460592ffb21SWarner Losh return ret; 2461592ffb21SWarner Losh } 2462592ffb21SWarner Losh 2463592ffb21SWarner Losh int drm_mode_dirtyfb_ioctl(struct drm_device *dev, 2464592ffb21SWarner Losh void *data, struct drm_file *file_priv) 2465592ffb21SWarner Losh { 2466592ffb21SWarner Losh struct drm_clip_rect __user *clips_ptr; 2467592ffb21SWarner Losh struct drm_clip_rect *clips = NULL; 2468592ffb21SWarner Losh struct drm_mode_fb_dirty_cmd *r = data; 2469592ffb21SWarner Losh struct drm_mode_object *obj; 2470592ffb21SWarner Losh struct drm_framebuffer *fb; 2471592ffb21SWarner Losh unsigned flags; 2472592ffb21SWarner Losh int num_clips; 2473592ffb21SWarner Losh int ret; 2474592ffb21SWarner Losh 2475592ffb21SWarner Losh if (!drm_core_check_feature(dev, DRIVER_MODESET)) 2476592ffb21SWarner Losh return -EINVAL; 2477592ffb21SWarner Losh 2478592ffb21SWarner Losh sx_xlock(&dev->mode_config.mutex); 2479592ffb21SWarner Losh obj = drm_mode_object_find(dev, r->fb_id, DRM_MODE_OBJECT_FB); 2480592ffb21SWarner Losh if (!obj) { 2481592ffb21SWarner Losh ret = -EINVAL; 2482592ffb21SWarner Losh goto out_err1; 2483592ffb21SWarner Losh } 2484592ffb21SWarner Losh fb = obj_to_fb(obj); 2485592ffb21SWarner Losh 2486592ffb21SWarner Losh num_clips = r->num_clips; 2487592ffb21SWarner Losh clips_ptr = (struct drm_clip_rect __user *)(unsigned long)r->clips_ptr; 2488592ffb21SWarner Losh 2489592ffb21SWarner Losh if (!num_clips != !clips_ptr) { 2490592ffb21SWarner Losh ret = -EINVAL; 2491592ffb21SWarner Losh goto out_err1; 2492592ffb21SWarner Losh } 2493592ffb21SWarner Losh 2494592ffb21SWarner Losh flags = DRM_MODE_FB_DIRTY_FLAGS & r->flags; 2495592ffb21SWarner Losh 2496592ffb21SWarner Losh /* If userspace annotates copy, clips must come in pairs */ 2497592ffb21SWarner Losh if (flags & DRM_MODE_FB_DIRTY_ANNOTATE_COPY && (num_clips % 2)) { 2498592ffb21SWarner Losh ret = -EINVAL; 2499592ffb21SWarner Losh goto out_err1; 2500592ffb21SWarner Losh } 2501592ffb21SWarner Losh 2502592ffb21SWarner Losh if (num_clips && clips_ptr) { 2503592ffb21SWarner Losh if (num_clips < 0 || num_clips > DRM_MODE_FB_DIRTY_MAX_CLIPS) { 2504592ffb21SWarner Losh ret = -EINVAL; 2505592ffb21SWarner Losh goto out_err1; 2506592ffb21SWarner Losh } 2507592ffb21SWarner Losh clips = malloc(num_clips * sizeof(*clips), DRM_MEM_KMS, 2508592ffb21SWarner Losh M_WAITOK | M_ZERO); 2509592ffb21SWarner Losh ret = copy_from_user(clips, clips_ptr, 2510592ffb21SWarner Losh num_clips * sizeof(*clips)); 2511592ffb21SWarner Losh if (ret) { 2512592ffb21SWarner Losh ret = -EFAULT; 2513592ffb21SWarner Losh goto out_err2; 2514592ffb21SWarner Losh } 2515592ffb21SWarner Losh } 2516592ffb21SWarner Losh 2517592ffb21SWarner Losh if (fb->funcs->dirty) { 2518592ffb21SWarner Losh ret = fb->funcs->dirty(fb, file_priv, flags, r->color, 2519592ffb21SWarner Losh clips, num_clips); 2520592ffb21SWarner Losh } else { 2521592ffb21SWarner Losh ret = -ENOSYS; 2522592ffb21SWarner Losh goto out_err2; 2523592ffb21SWarner Losh } 2524592ffb21SWarner Losh 2525592ffb21SWarner Losh out_err2: 2526592ffb21SWarner Losh free(clips, DRM_MEM_KMS); 2527592ffb21SWarner Losh out_err1: 2528592ffb21SWarner Losh sx_xunlock(&dev->mode_config.mutex); 2529592ffb21SWarner Losh return ret; 2530592ffb21SWarner Losh } 2531592ffb21SWarner Losh 2532592ffb21SWarner Losh 2533592ffb21SWarner Losh /** 2534592ffb21SWarner Losh * drm_fb_release - remove and free the FBs on this file 2535592ffb21SWarner Losh * @filp: file * from the ioctl 2536592ffb21SWarner Losh * 2537592ffb21SWarner Losh * LOCKING: 2538592ffb21SWarner Losh * Takes mode config lock. 2539592ffb21SWarner Losh * 2540592ffb21SWarner Losh * Destroy all the FBs associated with @filp. 2541592ffb21SWarner Losh * 2542592ffb21SWarner Losh * Called by the user via ioctl. 2543592ffb21SWarner Losh * 2544592ffb21SWarner Losh * RETURNS: 2545592ffb21SWarner Losh * Zero on success, errno on failure. 2546592ffb21SWarner Losh */ 2547592ffb21SWarner Losh void drm_fb_release(struct drm_file *priv) 2548592ffb21SWarner Losh { 2549592ffb21SWarner Losh struct drm_device *dev = priv->minor->dev; 2550592ffb21SWarner Losh struct drm_framebuffer *fb, *tfb; 2551592ffb21SWarner Losh 2552592ffb21SWarner Losh sx_xlock(&dev->mode_config.mutex); 2553592ffb21SWarner Losh list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) { 2554592ffb21SWarner Losh drm_framebuffer_remove(fb); 2555592ffb21SWarner Losh } 2556592ffb21SWarner Losh sx_xunlock(&dev->mode_config.mutex); 2557592ffb21SWarner Losh } 2558592ffb21SWarner Losh 2559592ffb21SWarner Losh /** 2560592ffb21SWarner Losh * drm_mode_attachmode - add a mode to the user mode list 2561592ffb21SWarner Losh * @dev: DRM device 2562592ffb21SWarner Losh * @connector: connector to add the mode to 2563592ffb21SWarner Losh * @mode: mode to add 2564592ffb21SWarner Losh * 2565592ffb21SWarner Losh * Add @mode to @connector's user mode list. 2566592ffb21SWarner Losh */ 2567592ffb21SWarner Losh static void drm_mode_attachmode(struct drm_device *dev, 2568592ffb21SWarner Losh struct drm_connector *connector, 2569592ffb21SWarner Losh struct drm_display_mode *mode) 2570592ffb21SWarner Losh { 2571592ffb21SWarner Losh list_add_tail(&mode->head, &connector->user_modes); 2572592ffb21SWarner Losh } 2573592ffb21SWarner Losh 2574592ffb21SWarner Losh int drm_mode_attachmode_crtc(struct drm_device *dev, struct drm_crtc *crtc, 2575592ffb21SWarner Losh const struct drm_display_mode *mode) 2576592ffb21SWarner Losh { 2577592ffb21SWarner Losh struct drm_connector *connector; 2578592ffb21SWarner Losh int ret = 0; 2579592ffb21SWarner Losh struct drm_display_mode *dup_mode, *next; 2580592ffb21SWarner Losh DRM_LIST_HEAD(list); 2581592ffb21SWarner Losh 2582592ffb21SWarner Losh list_for_each_entry(connector, &dev->mode_config.connector_list, head) { 2583592ffb21SWarner Losh if (!connector->encoder) 2584592ffb21SWarner Losh continue; 2585592ffb21SWarner Losh if (connector->encoder->crtc == crtc) { 2586592ffb21SWarner Losh dup_mode = drm_mode_duplicate(dev, mode); 2587592ffb21SWarner Losh if (!dup_mode) { 2588592ffb21SWarner Losh ret = -ENOMEM; 2589592ffb21SWarner Losh goto out; 2590592ffb21SWarner Losh } 2591592ffb21SWarner Losh list_add_tail(&dup_mode->head, &list); 2592592ffb21SWarner Losh } 2593592ffb21SWarner Losh } 2594592ffb21SWarner Losh 2595592ffb21SWarner Losh list_for_each_entry(connector, &dev->mode_config.connector_list, head) { 2596592ffb21SWarner Losh if (!connector->encoder) 2597592ffb21SWarner Losh continue; 2598592ffb21SWarner Losh if (connector->encoder->crtc == crtc) 2599592ffb21SWarner Losh list_move_tail(list.next, &connector->user_modes); 2600592ffb21SWarner Losh } 2601592ffb21SWarner Losh 2602592ffb21SWarner Losh MPASS(!list_empty(&list)); 2603592ffb21SWarner Losh 2604592ffb21SWarner Losh out: 2605592ffb21SWarner Losh list_for_each_entry_safe(dup_mode, next, &list, head) 2606592ffb21SWarner Losh drm_mode_destroy(dev, dup_mode); 2607592ffb21SWarner Losh 2608592ffb21SWarner Losh return ret; 2609592ffb21SWarner Losh } 2610592ffb21SWarner Losh EXPORT_SYMBOL(drm_mode_attachmode_crtc); 2611592ffb21SWarner Losh 2612592ffb21SWarner Losh static int drm_mode_detachmode(struct drm_device *dev, 2613592ffb21SWarner Losh struct drm_connector *connector, 2614592ffb21SWarner Losh struct drm_display_mode *mode) 2615592ffb21SWarner Losh { 2616592ffb21SWarner Losh int found = 0; 2617592ffb21SWarner Losh int ret = 0; 2618592ffb21SWarner Losh struct drm_display_mode *match_mode, *t; 2619592ffb21SWarner Losh 2620592ffb21SWarner Losh list_for_each_entry_safe(match_mode, t, &connector->user_modes, head) { 2621592ffb21SWarner Losh if (drm_mode_equal(match_mode, mode)) { 2622592ffb21SWarner Losh list_del(&match_mode->head); 2623592ffb21SWarner Losh drm_mode_destroy(dev, match_mode); 2624592ffb21SWarner Losh found = 1; 2625592ffb21SWarner Losh break; 2626592ffb21SWarner Losh } 2627592ffb21SWarner Losh } 2628592ffb21SWarner Losh 2629592ffb21SWarner Losh if (!found) 2630592ffb21SWarner Losh ret = -EINVAL; 2631592ffb21SWarner Losh 2632592ffb21SWarner Losh return ret; 2633592ffb21SWarner Losh } 2634592ffb21SWarner Losh 2635592ffb21SWarner Losh int drm_mode_detachmode_crtc(struct drm_device *dev, struct drm_display_mode *mode) 2636592ffb21SWarner Losh { 2637592ffb21SWarner Losh struct drm_connector *connector; 2638592ffb21SWarner Losh 2639592ffb21SWarner Losh list_for_each_entry(connector, &dev->mode_config.connector_list, head) { 2640592ffb21SWarner Losh drm_mode_detachmode(dev, connector, mode); 2641592ffb21SWarner Losh } 2642592ffb21SWarner Losh return 0; 2643592ffb21SWarner Losh } 2644592ffb21SWarner Losh EXPORT_SYMBOL(drm_mode_detachmode_crtc); 2645592ffb21SWarner Losh 2646592ffb21SWarner Losh /** 2647592ffb21SWarner Losh * drm_fb_attachmode - Attach a user mode to an connector 2648592ffb21SWarner Losh * @inode: inode from the ioctl 2649592ffb21SWarner Losh * @filp: file * from the ioctl 2650592ffb21SWarner Losh * @cmd: cmd from ioctl 2651592ffb21SWarner Losh * @arg: arg from ioctl 2652592ffb21SWarner Losh * 2653592ffb21SWarner Losh * This attaches a user specified mode to an connector. 2654592ffb21SWarner Losh * Called by the user via ioctl. 2655592ffb21SWarner Losh * 2656592ffb21SWarner Losh * RETURNS: 2657592ffb21SWarner Losh * Zero on success, errno on failure. 2658592ffb21SWarner Losh */ 2659592ffb21SWarner Losh int drm_mode_attachmode_ioctl(struct drm_device *dev, 2660592ffb21SWarner Losh void *data, struct drm_file *file_priv) 2661592ffb21SWarner Losh { 2662592ffb21SWarner Losh struct drm_mode_mode_cmd *mode_cmd = data; 2663592ffb21SWarner Losh struct drm_connector *connector; 2664592ffb21SWarner Losh struct drm_display_mode *mode; 2665592ffb21SWarner Losh struct drm_mode_object *obj; 2666592ffb21SWarner Losh struct drm_mode_modeinfo *umode = &mode_cmd->mode; 2667592ffb21SWarner Losh int ret; 2668592ffb21SWarner Losh 2669592ffb21SWarner Losh if (!drm_core_check_feature(dev, DRIVER_MODESET)) 2670592ffb21SWarner Losh return -EINVAL; 2671592ffb21SWarner Losh 2672592ffb21SWarner Losh sx_xlock(&dev->mode_config.mutex); 2673592ffb21SWarner Losh 2674592ffb21SWarner Losh obj = drm_mode_object_find(dev, mode_cmd->connector_id, DRM_MODE_OBJECT_CONNECTOR); 2675592ffb21SWarner Losh if (!obj) { 2676592ffb21SWarner Losh ret = -EINVAL; 2677592ffb21SWarner Losh goto out; 2678592ffb21SWarner Losh } 2679592ffb21SWarner Losh connector = obj_to_connector(obj); 2680592ffb21SWarner Losh 2681592ffb21SWarner Losh mode = drm_mode_create(dev); 2682592ffb21SWarner Losh if (!mode) { 2683592ffb21SWarner Losh ret = -ENOMEM; 2684592ffb21SWarner Losh goto out; 2685592ffb21SWarner Losh } 2686592ffb21SWarner Losh 2687592ffb21SWarner Losh ret = drm_crtc_convert_umode(mode, umode); 2688592ffb21SWarner Losh if (ret) { 2689592ffb21SWarner Losh DRM_DEBUG_KMS("Invalid mode\n"); 2690592ffb21SWarner Losh drm_mode_destroy(dev, mode); 2691592ffb21SWarner Losh goto out; 2692592ffb21SWarner Losh } 2693592ffb21SWarner Losh 2694592ffb21SWarner Losh drm_mode_attachmode(dev, connector, mode); 2695592ffb21SWarner Losh out: 2696592ffb21SWarner Losh sx_xunlock(&dev->mode_config.mutex); 2697592ffb21SWarner Losh return ret; 2698592ffb21SWarner Losh } 2699592ffb21SWarner Losh 2700592ffb21SWarner Losh 2701592ffb21SWarner Losh /** 2702592ffb21SWarner Losh * drm_fb_detachmode - Detach a user specified mode from an connector 2703592ffb21SWarner Losh * @inode: inode from the ioctl 2704592ffb21SWarner Losh * @filp: file * from the ioctl 2705592ffb21SWarner Losh * @cmd: cmd from ioctl 2706592ffb21SWarner Losh * @arg: arg from ioctl 2707592ffb21SWarner Losh * 2708592ffb21SWarner Losh * Called by the user via ioctl. 2709592ffb21SWarner Losh * 2710592ffb21SWarner Losh * RETURNS: 2711592ffb21SWarner Losh * Zero on success, errno on failure. 2712592ffb21SWarner Losh */ 2713592ffb21SWarner Losh int drm_mode_detachmode_ioctl(struct drm_device *dev, 2714592ffb21SWarner Losh void *data, struct drm_file *file_priv) 2715592ffb21SWarner Losh { 2716592ffb21SWarner Losh struct drm_mode_object *obj; 2717592ffb21SWarner Losh struct drm_mode_mode_cmd *mode_cmd = data; 2718592ffb21SWarner Losh struct drm_connector *connector; 2719592ffb21SWarner Losh struct drm_display_mode mode; 2720592ffb21SWarner Losh struct drm_mode_modeinfo *umode = &mode_cmd->mode; 2721592ffb21SWarner Losh int ret; 2722592ffb21SWarner Losh 2723592ffb21SWarner Losh if (!drm_core_check_feature(dev, DRIVER_MODESET)) 2724592ffb21SWarner Losh return -EINVAL; 2725592ffb21SWarner Losh 2726592ffb21SWarner Losh sx_xlock(&dev->mode_config.mutex); 2727592ffb21SWarner Losh 2728592ffb21SWarner Losh obj = drm_mode_object_find(dev, mode_cmd->connector_id, DRM_MODE_OBJECT_CONNECTOR); 2729592ffb21SWarner Losh if (!obj) { 2730592ffb21SWarner Losh ret = -EINVAL; 2731592ffb21SWarner Losh goto out; 2732592ffb21SWarner Losh } 2733592ffb21SWarner Losh connector = obj_to_connector(obj); 2734592ffb21SWarner Losh 2735592ffb21SWarner Losh ret = drm_crtc_convert_umode(&mode, umode); 2736592ffb21SWarner Losh if (ret) { 2737592ffb21SWarner Losh DRM_DEBUG_KMS("Invalid mode\n"); 2738592ffb21SWarner Losh goto out; 2739592ffb21SWarner Losh } 2740592ffb21SWarner Losh 2741592ffb21SWarner Losh ret = drm_mode_detachmode(dev, connector, &mode); 2742592ffb21SWarner Losh out: 2743592ffb21SWarner Losh sx_xunlock(&dev->mode_config.mutex); 2744592ffb21SWarner Losh return ret; 2745592ffb21SWarner Losh } 2746592ffb21SWarner Losh 2747592ffb21SWarner Losh struct drm_property *drm_property_create(struct drm_device *dev, int flags, 2748592ffb21SWarner Losh const char *name, int num_values) 2749592ffb21SWarner Losh { 2750592ffb21SWarner Losh struct drm_property *property = NULL; 2751592ffb21SWarner Losh int ret; 2752592ffb21SWarner Losh 2753592ffb21SWarner Losh property = malloc(sizeof(struct drm_property), DRM_MEM_KMS, 2754592ffb21SWarner Losh M_WAITOK | M_ZERO); 2755592ffb21SWarner Losh 2756*6dbf3acaSZhenlei Huang if (num_values) 2757592ffb21SWarner Losh property->values = malloc(sizeof(uint64_t)*num_values, DRM_MEM_KMS, 2758592ffb21SWarner Losh M_WAITOK | M_ZERO); 2759592ffb21SWarner Losh 2760592ffb21SWarner Losh ret = drm_mode_object_get(dev, &property->base, DRM_MODE_OBJECT_PROPERTY); 2761592ffb21SWarner Losh if (ret) 2762592ffb21SWarner Losh goto fail; 2763592ffb21SWarner Losh 2764592ffb21SWarner Losh property->flags = flags; 2765592ffb21SWarner Losh property->num_values = num_values; 2766592ffb21SWarner Losh INIT_LIST_HEAD(&property->enum_blob_list); 2767592ffb21SWarner Losh 2768592ffb21SWarner Losh if (name) { 2769592ffb21SWarner Losh strncpy(property->name, name, DRM_PROP_NAME_LEN); 2770592ffb21SWarner Losh property->name[DRM_PROP_NAME_LEN-1] = '\0'; 2771592ffb21SWarner Losh } 2772592ffb21SWarner Losh 2773592ffb21SWarner Losh list_add_tail(&property->head, &dev->mode_config.property_list); 2774592ffb21SWarner Losh return property; 2775592ffb21SWarner Losh fail: 2776592ffb21SWarner Losh free(property->values, DRM_MEM_KMS); 2777592ffb21SWarner Losh free(property, DRM_MEM_KMS); 2778592ffb21SWarner Losh return NULL; 2779592ffb21SWarner Losh } 2780592ffb21SWarner Losh EXPORT_SYMBOL(drm_property_create); 2781592ffb21SWarner Losh 2782592ffb21SWarner Losh struct drm_property *drm_property_create_enum(struct drm_device *dev, int flags, 2783592ffb21SWarner Losh const char *name, 2784592ffb21SWarner Losh const struct drm_prop_enum_list *props, 2785592ffb21SWarner Losh int num_values) 2786592ffb21SWarner Losh { 2787592ffb21SWarner Losh struct drm_property *property; 2788592ffb21SWarner Losh int i, ret; 2789592ffb21SWarner Losh 2790592ffb21SWarner Losh flags |= DRM_MODE_PROP_ENUM; 2791592ffb21SWarner Losh 2792592ffb21SWarner Losh property = drm_property_create(dev, flags, name, num_values); 2793592ffb21SWarner Losh if (!property) 2794592ffb21SWarner Losh return NULL; 2795592ffb21SWarner Losh 2796592ffb21SWarner Losh for (i = 0; i < num_values; i++) { 2797592ffb21SWarner Losh ret = drm_property_add_enum(property, i, 2798592ffb21SWarner Losh props[i].type, 2799592ffb21SWarner Losh props[i].name); 2800592ffb21SWarner Losh if (ret) { 2801592ffb21SWarner Losh drm_property_destroy(dev, property); 2802592ffb21SWarner Losh return NULL; 2803592ffb21SWarner Losh } 2804592ffb21SWarner Losh } 2805592ffb21SWarner Losh 2806592ffb21SWarner Losh return property; 2807592ffb21SWarner Losh } 2808592ffb21SWarner Losh EXPORT_SYMBOL(drm_property_create_enum); 2809592ffb21SWarner Losh 2810592ffb21SWarner Losh struct drm_property *drm_property_create_bitmask(struct drm_device *dev, 2811592ffb21SWarner Losh int flags, const char *name, 2812592ffb21SWarner Losh const struct drm_prop_enum_list *props, 2813592ffb21SWarner Losh int num_values) 2814592ffb21SWarner Losh { 2815592ffb21SWarner Losh struct drm_property *property; 2816592ffb21SWarner Losh int i, ret; 2817592ffb21SWarner Losh 2818592ffb21SWarner Losh flags |= DRM_MODE_PROP_BITMASK; 2819592ffb21SWarner Losh 2820592ffb21SWarner Losh property = drm_property_create(dev, flags, name, num_values); 2821592ffb21SWarner Losh if (!property) 2822592ffb21SWarner Losh return NULL; 2823592ffb21SWarner Losh 2824592ffb21SWarner Losh for (i = 0; i < num_values; i++) { 2825592ffb21SWarner Losh ret = drm_property_add_enum(property, i, 2826592ffb21SWarner Losh props[i].type, 2827592ffb21SWarner Losh props[i].name); 2828592ffb21SWarner Losh if (ret) { 2829592ffb21SWarner Losh drm_property_destroy(dev, property); 2830592ffb21SWarner Losh return NULL; 2831592ffb21SWarner Losh } 2832592ffb21SWarner Losh } 2833592ffb21SWarner Losh 2834592ffb21SWarner Losh return property; 2835592ffb21SWarner Losh } 2836592ffb21SWarner Losh EXPORT_SYMBOL(drm_property_create_bitmask); 2837592ffb21SWarner Losh 2838592ffb21SWarner Losh struct drm_property *drm_property_create_range(struct drm_device *dev, int flags, 2839592ffb21SWarner Losh const char *name, 2840592ffb21SWarner Losh uint64_t min, uint64_t max) 2841592ffb21SWarner Losh { 2842592ffb21SWarner Losh struct drm_property *property; 2843592ffb21SWarner Losh 2844592ffb21SWarner Losh flags |= DRM_MODE_PROP_RANGE; 2845592ffb21SWarner Losh 2846592ffb21SWarner Losh property = drm_property_create(dev, flags, name, 2); 2847592ffb21SWarner Losh if (!property) 2848592ffb21SWarner Losh return NULL; 2849592ffb21SWarner Losh 2850592ffb21SWarner Losh property->values[0] = min; 2851592ffb21SWarner Losh property->values[1] = max; 2852592ffb21SWarner Losh 2853592ffb21SWarner Losh return property; 2854592ffb21SWarner Losh } 2855592ffb21SWarner Losh EXPORT_SYMBOL(drm_property_create_range); 2856592ffb21SWarner Losh 2857592ffb21SWarner Losh int drm_property_add_enum(struct drm_property *property, int index, 2858592ffb21SWarner Losh uint64_t value, const char *name) 2859592ffb21SWarner Losh { 2860592ffb21SWarner Losh struct drm_property_enum *prop_enum; 2861592ffb21SWarner Losh 2862592ffb21SWarner Losh if (!(property->flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK))) 2863592ffb21SWarner Losh return -EINVAL; 2864592ffb21SWarner Losh 2865592ffb21SWarner Losh /* 2866592ffb21SWarner Losh * Bitmask enum properties have the additional constraint of values 2867592ffb21SWarner Losh * from 0 to 63 2868592ffb21SWarner Losh */ 2869592ffb21SWarner Losh if ((property->flags & DRM_MODE_PROP_BITMASK) && (value > 63)) 2870592ffb21SWarner Losh return -EINVAL; 2871592ffb21SWarner Losh 2872592ffb21SWarner Losh if (!list_empty(&property->enum_blob_list)) { 2873592ffb21SWarner Losh list_for_each_entry(prop_enum, &property->enum_blob_list, head) { 2874592ffb21SWarner Losh if (prop_enum->value == value) { 2875592ffb21SWarner Losh strncpy(prop_enum->name, name, DRM_PROP_NAME_LEN); 2876592ffb21SWarner Losh prop_enum->name[DRM_PROP_NAME_LEN-1] = '\0'; 2877592ffb21SWarner Losh return 0; 2878592ffb21SWarner Losh } 2879592ffb21SWarner Losh } 2880592ffb21SWarner Losh } 2881592ffb21SWarner Losh 2882592ffb21SWarner Losh prop_enum = malloc(sizeof(struct drm_property_enum), DRM_MEM_KMS, 2883592ffb21SWarner Losh M_WAITOK | M_ZERO); 2884592ffb21SWarner Losh strncpy(prop_enum->name, name, DRM_PROP_NAME_LEN); 2885592ffb21SWarner Losh prop_enum->name[DRM_PROP_NAME_LEN-1] = '\0'; 2886592ffb21SWarner Losh prop_enum->value = value; 2887592ffb21SWarner Losh 2888592ffb21SWarner Losh property->values[index] = value; 2889592ffb21SWarner Losh list_add_tail(&prop_enum->head, &property->enum_blob_list); 2890592ffb21SWarner Losh return 0; 2891592ffb21SWarner Losh } 2892592ffb21SWarner Losh EXPORT_SYMBOL(drm_property_add_enum); 2893592ffb21SWarner Losh 2894592ffb21SWarner Losh void drm_property_destroy(struct drm_device *dev, struct drm_property *property) 2895592ffb21SWarner Losh { 2896592ffb21SWarner Losh struct drm_property_enum *prop_enum, *pt; 2897592ffb21SWarner Losh 2898592ffb21SWarner Losh list_for_each_entry_safe(prop_enum, pt, &property->enum_blob_list, head) { 2899592ffb21SWarner Losh list_del(&prop_enum->head); 2900592ffb21SWarner Losh free(prop_enum, DRM_MEM_KMS); 2901592ffb21SWarner Losh } 2902592ffb21SWarner Losh 2903592ffb21SWarner Losh if (property->num_values) 2904592ffb21SWarner Losh free(property->values, DRM_MEM_KMS); 2905592ffb21SWarner Losh drm_mode_object_put(dev, &property->base); 2906592ffb21SWarner Losh list_del(&property->head); 2907592ffb21SWarner Losh free(property, DRM_MEM_KMS); 2908592ffb21SWarner Losh } 2909592ffb21SWarner Losh EXPORT_SYMBOL(drm_property_destroy); 2910592ffb21SWarner Losh 2911592ffb21SWarner Losh void drm_object_attach_property(struct drm_mode_object *obj, 2912592ffb21SWarner Losh struct drm_property *property, 2913592ffb21SWarner Losh uint64_t init_val) 2914592ffb21SWarner Losh { 2915592ffb21SWarner Losh int count = obj->properties->count; 2916592ffb21SWarner Losh 2917592ffb21SWarner Losh if (count == DRM_OBJECT_MAX_PROPERTY) { 2918592ffb21SWarner Losh DRM_WARNING("Failed to attach object property (type: 0x%x). Please " 2919592ffb21SWarner Losh "increase DRM_OBJECT_MAX_PROPERTY by 1 for each time " 2920592ffb21SWarner Losh "you see this message on the same object type.\n", 2921592ffb21SWarner Losh obj->type); 2922592ffb21SWarner Losh return; 2923592ffb21SWarner Losh } 2924592ffb21SWarner Losh 2925592ffb21SWarner Losh obj->properties->ids[count] = property->base.id; 2926592ffb21SWarner Losh obj->properties->values[count] = init_val; 2927592ffb21SWarner Losh obj->properties->count++; 2928592ffb21SWarner Losh } 2929592ffb21SWarner Losh EXPORT_SYMBOL(drm_object_attach_property); 2930592ffb21SWarner Losh 2931592ffb21SWarner Losh int drm_object_property_set_value(struct drm_mode_object *obj, 2932592ffb21SWarner Losh struct drm_property *property, uint64_t val) 2933592ffb21SWarner Losh { 2934592ffb21SWarner Losh int i; 2935592ffb21SWarner Losh 2936592ffb21SWarner Losh for (i = 0; i < obj->properties->count; i++) { 2937592ffb21SWarner Losh if (obj->properties->ids[i] == property->base.id) { 2938592ffb21SWarner Losh obj->properties->values[i] = val; 2939592ffb21SWarner Losh return 0; 2940592ffb21SWarner Losh } 2941592ffb21SWarner Losh } 2942592ffb21SWarner Losh 2943592ffb21SWarner Losh return -EINVAL; 2944592ffb21SWarner Losh } 2945592ffb21SWarner Losh EXPORT_SYMBOL(drm_object_property_set_value); 2946592ffb21SWarner Losh 2947592ffb21SWarner Losh int drm_object_property_get_value(struct drm_mode_object *obj, 2948592ffb21SWarner Losh struct drm_property *property, uint64_t *val) 2949592ffb21SWarner Losh { 2950592ffb21SWarner Losh int i; 2951592ffb21SWarner Losh 2952592ffb21SWarner Losh for (i = 0; i < obj->properties->count; i++) { 2953592ffb21SWarner Losh if (obj->properties->ids[i] == property->base.id) { 2954592ffb21SWarner Losh *val = obj->properties->values[i]; 2955592ffb21SWarner Losh return 0; 2956592ffb21SWarner Losh } 2957592ffb21SWarner Losh } 2958592ffb21SWarner Losh 2959592ffb21SWarner Losh return -EINVAL; 2960592ffb21SWarner Losh } 2961592ffb21SWarner Losh EXPORT_SYMBOL(drm_object_property_get_value); 2962592ffb21SWarner Losh 2963592ffb21SWarner Losh int drm_mode_getproperty_ioctl(struct drm_device *dev, 2964592ffb21SWarner Losh void *data, struct drm_file *file_priv) 2965592ffb21SWarner Losh { 2966592ffb21SWarner Losh struct drm_mode_object *obj; 2967592ffb21SWarner Losh struct drm_mode_get_property *out_resp = data; 2968592ffb21SWarner Losh struct drm_property *property; 2969592ffb21SWarner Losh int enum_count = 0; 2970592ffb21SWarner Losh int blob_count = 0; 2971592ffb21SWarner Losh int value_count = 0; 2972592ffb21SWarner Losh int ret = 0, i; 2973592ffb21SWarner Losh int copied; 2974592ffb21SWarner Losh struct drm_property_enum *prop_enum; 2975592ffb21SWarner Losh struct drm_mode_property_enum __user *enum_ptr; 2976592ffb21SWarner Losh struct drm_property_blob *prop_blob; 2977592ffb21SWarner Losh uint32_t __user *blob_id_ptr; 2978592ffb21SWarner Losh uint64_t __user *values_ptr; 2979592ffb21SWarner Losh uint32_t __user *blob_length_ptr; 2980592ffb21SWarner Losh 2981592ffb21SWarner Losh if (!drm_core_check_feature(dev, DRIVER_MODESET)) 2982592ffb21SWarner Losh return -EINVAL; 2983592ffb21SWarner Losh 2984592ffb21SWarner Losh sx_xlock(&dev->mode_config.mutex); 2985592ffb21SWarner Losh obj = drm_mode_object_find(dev, out_resp->prop_id, DRM_MODE_OBJECT_PROPERTY); 2986592ffb21SWarner Losh if (!obj) { 2987592ffb21SWarner Losh ret = -EINVAL; 2988592ffb21SWarner Losh goto done; 2989592ffb21SWarner Losh } 2990592ffb21SWarner Losh property = obj_to_property(obj); 2991592ffb21SWarner Losh 2992592ffb21SWarner Losh if (property->flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK)) { 2993592ffb21SWarner Losh list_for_each_entry(prop_enum, &property->enum_blob_list, head) 2994592ffb21SWarner Losh enum_count++; 2995592ffb21SWarner Losh } else if (property->flags & DRM_MODE_PROP_BLOB) { 2996592ffb21SWarner Losh list_for_each_entry(prop_blob, &property->enum_blob_list, head) 2997592ffb21SWarner Losh blob_count++; 2998592ffb21SWarner Losh } 2999592ffb21SWarner Losh 3000592ffb21SWarner Losh value_count = property->num_values; 3001592ffb21SWarner Losh 3002592ffb21SWarner Losh strncpy(out_resp->name, property->name, DRM_PROP_NAME_LEN); 3003592ffb21SWarner Losh out_resp->name[DRM_PROP_NAME_LEN-1] = 0; 3004592ffb21SWarner Losh out_resp->flags = property->flags; 3005592ffb21SWarner Losh 3006592ffb21SWarner Losh if ((out_resp->count_values >= value_count) && value_count) { 3007592ffb21SWarner Losh values_ptr = (uint64_t __user *)(unsigned long)out_resp->values_ptr; 3008592ffb21SWarner Losh for (i = 0; i < value_count; i++) { 3009592ffb21SWarner Losh if (copy_to_user(values_ptr + i, &property->values[i], sizeof(uint64_t))) { 3010592ffb21SWarner Losh ret = -EFAULT; 3011592ffb21SWarner Losh goto done; 3012592ffb21SWarner Losh } 3013592ffb21SWarner Losh } 3014592ffb21SWarner Losh } 3015592ffb21SWarner Losh out_resp->count_values = value_count; 3016592ffb21SWarner Losh 3017592ffb21SWarner Losh if (property->flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK)) { 3018592ffb21SWarner Losh if ((out_resp->count_enum_blobs >= enum_count) && enum_count) { 3019592ffb21SWarner Losh copied = 0; 3020592ffb21SWarner Losh enum_ptr = (struct drm_mode_property_enum __user *)(unsigned long)out_resp->enum_blob_ptr; 3021592ffb21SWarner Losh list_for_each_entry(prop_enum, &property->enum_blob_list, head) { 3022592ffb21SWarner Losh 3023592ffb21SWarner Losh if (copy_to_user(&enum_ptr[copied].value, &prop_enum->value, sizeof(uint64_t))) { 3024592ffb21SWarner Losh ret = -EFAULT; 3025592ffb21SWarner Losh goto done; 3026592ffb21SWarner Losh } 3027592ffb21SWarner Losh 3028592ffb21SWarner Losh if (copy_to_user(&enum_ptr[copied].name, 3029592ffb21SWarner Losh &prop_enum->name, DRM_PROP_NAME_LEN)) { 3030592ffb21SWarner Losh ret = -EFAULT; 3031592ffb21SWarner Losh goto done; 3032592ffb21SWarner Losh } 3033592ffb21SWarner Losh copied++; 3034592ffb21SWarner Losh } 3035592ffb21SWarner Losh } 3036592ffb21SWarner Losh out_resp->count_enum_blobs = enum_count; 3037592ffb21SWarner Losh } 3038592ffb21SWarner Losh 3039592ffb21SWarner Losh if (property->flags & DRM_MODE_PROP_BLOB) { 3040592ffb21SWarner Losh if ((out_resp->count_enum_blobs >= blob_count) && blob_count) { 3041592ffb21SWarner Losh copied = 0; 3042592ffb21SWarner Losh blob_id_ptr = (uint32_t __user *)(unsigned long)out_resp->enum_blob_ptr; 3043592ffb21SWarner Losh blob_length_ptr = (uint32_t __user *)(unsigned long)out_resp->values_ptr; 3044592ffb21SWarner Losh 3045592ffb21SWarner Losh list_for_each_entry(prop_blob, &property->enum_blob_list, head) { 3046592ffb21SWarner Losh if (put_user(prop_blob->base.id, blob_id_ptr + copied)) { 3047592ffb21SWarner Losh ret = -EFAULT; 3048592ffb21SWarner Losh goto done; 3049592ffb21SWarner Losh } 3050592ffb21SWarner Losh 3051592ffb21SWarner Losh if (put_user(prop_blob->length, blob_length_ptr + copied)) { 3052592ffb21SWarner Losh ret = -EFAULT; 3053592ffb21SWarner Losh goto done; 3054592ffb21SWarner Losh } 3055592ffb21SWarner Losh 3056592ffb21SWarner Losh copied++; 3057592ffb21SWarner Losh } 3058592ffb21SWarner Losh } 3059592ffb21SWarner Losh out_resp->count_enum_blobs = blob_count; 3060592ffb21SWarner Losh } 3061592ffb21SWarner Losh done: 3062592ffb21SWarner Losh sx_xunlock(&dev->mode_config.mutex); 3063592ffb21SWarner Losh return ret; 3064592ffb21SWarner Losh } 3065592ffb21SWarner Losh 3066592ffb21SWarner Losh static struct drm_property_blob *drm_property_create_blob(struct drm_device *dev, int length, 3067592ffb21SWarner Losh void *data) 3068592ffb21SWarner Losh { 3069592ffb21SWarner Losh struct drm_property_blob *blob; 3070592ffb21SWarner Losh int ret; 3071592ffb21SWarner Losh 3072592ffb21SWarner Losh if (!length || !data) 3073592ffb21SWarner Losh return NULL; 3074592ffb21SWarner Losh 3075592ffb21SWarner Losh blob = malloc(sizeof(struct drm_property_blob)+length, DRM_MEM_KMS, 3076592ffb21SWarner Losh M_WAITOK | M_ZERO); 3077592ffb21SWarner Losh ret = drm_mode_object_get(dev, &blob->base, DRM_MODE_OBJECT_BLOB); 3078592ffb21SWarner Losh if (ret) { 3079592ffb21SWarner Losh free(blob, DRM_MEM_KMS); 3080592ffb21SWarner Losh return NULL; 3081592ffb21SWarner Losh } 3082592ffb21SWarner Losh 3083592ffb21SWarner Losh blob->length = length; 3084592ffb21SWarner Losh 3085592ffb21SWarner Losh memcpy(blob->data, data, length); 3086592ffb21SWarner Losh 3087592ffb21SWarner Losh list_add_tail(&blob->head, &dev->mode_config.property_blob_list); 3088592ffb21SWarner Losh return blob; 3089592ffb21SWarner Losh } 3090592ffb21SWarner Losh 3091592ffb21SWarner Losh static void drm_property_destroy_blob(struct drm_device *dev, 3092592ffb21SWarner Losh struct drm_property_blob *blob) 3093592ffb21SWarner Losh { 3094592ffb21SWarner Losh drm_mode_object_put(dev, &blob->base); 3095592ffb21SWarner Losh list_del(&blob->head); 3096592ffb21SWarner Losh free(blob, DRM_MEM_KMS); 3097592ffb21SWarner Losh } 3098592ffb21SWarner Losh 3099592ffb21SWarner Losh int drm_mode_getblob_ioctl(struct drm_device *dev, 3100592ffb21SWarner Losh void *data, struct drm_file *file_priv) 3101592ffb21SWarner Losh { 3102592ffb21SWarner Losh struct drm_mode_object *obj; 3103592ffb21SWarner Losh struct drm_mode_get_blob *out_resp = data; 3104592ffb21SWarner Losh struct drm_property_blob *blob; 3105592ffb21SWarner Losh int ret = 0; 3106592ffb21SWarner Losh void __user *blob_ptr; 3107592ffb21SWarner Losh 3108592ffb21SWarner Losh if (!drm_core_check_feature(dev, DRIVER_MODESET)) 3109592ffb21SWarner Losh return -EINVAL; 3110592ffb21SWarner Losh 3111592ffb21SWarner Losh sx_xlock(&dev->mode_config.mutex); 3112592ffb21SWarner Losh obj = drm_mode_object_find(dev, out_resp->blob_id, DRM_MODE_OBJECT_BLOB); 3113592ffb21SWarner Losh if (!obj) { 3114592ffb21SWarner Losh ret = -EINVAL; 3115592ffb21SWarner Losh goto done; 3116592ffb21SWarner Losh } 3117592ffb21SWarner Losh blob = obj_to_blob(obj); 3118592ffb21SWarner Losh 3119592ffb21SWarner Losh if (out_resp->length == blob->length) { 3120592ffb21SWarner Losh blob_ptr = (void __user *)(unsigned long)out_resp->data; 3121592ffb21SWarner Losh if (copy_to_user(blob_ptr, blob->data, blob->length)){ 3122592ffb21SWarner Losh ret = -EFAULT; 3123592ffb21SWarner Losh goto done; 3124592ffb21SWarner Losh } 3125592ffb21SWarner Losh } 3126592ffb21SWarner Losh out_resp->length = blob->length; 3127592ffb21SWarner Losh 3128592ffb21SWarner Losh done: 3129592ffb21SWarner Losh sx_xunlock(&dev->mode_config.mutex); 3130592ffb21SWarner Losh return ret; 3131592ffb21SWarner Losh } 3132592ffb21SWarner Losh 3133592ffb21SWarner Losh int drm_mode_connector_update_edid_property(struct drm_connector *connector, 3134592ffb21SWarner Losh struct edid *edid) 3135592ffb21SWarner Losh { 3136592ffb21SWarner Losh struct drm_device *dev = connector->dev; 3137592ffb21SWarner Losh int ret, size; 3138592ffb21SWarner Losh 3139592ffb21SWarner Losh if (connector->edid_blob_ptr) 3140592ffb21SWarner Losh drm_property_destroy_blob(dev, connector->edid_blob_ptr); 3141592ffb21SWarner Losh 3142592ffb21SWarner Losh /* Delete edid, when there is none. */ 3143592ffb21SWarner Losh if (!edid) { 3144592ffb21SWarner Losh connector->edid_blob_ptr = NULL; 3145592ffb21SWarner Losh ret = drm_object_property_set_value(&connector->base, dev->mode_config.edid_property, 0); 3146592ffb21SWarner Losh return ret; 3147592ffb21SWarner Losh } 3148592ffb21SWarner Losh 3149592ffb21SWarner Losh size = EDID_LENGTH * (1 + edid->extensions); 3150592ffb21SWarner Losh connector->edid_blob_ptr = drm_property_create_blob(connector->dev, 3151592ffb21SWarner Losh size, edid); 3152592ffb21SWarner Losh if (!connector->edid_blob_ptr) 3153592ffb21SWarner Losh return -EINVAL; 3154592ffb21SWarner Losh 3155592ffb21SWarner Losh ret = drm_object_property_set_value(&connector->base, 3156592ffb21SWarner Losh dev->mode_config.edid_property, 3157592ffb21SWarner Losh connector->edid_blob_ptr->base.id); 3158592ffb21SWarner Losh 3159592ffb21SWarner Losh return ret; 3160592ffb21SWarner Losh } 3161592ffb21SWarner Losh EXPORT_SYMBOL(drm_mode_connector_update_edid_property); 3162592ffb21SWarner Losh 3163592ffb21SWarner Losh static bool drm_property_change_is_valid(struct drm_property *property, 3164592ffb21SWarner Losh uint64_t value) 3165592ffb21SWarner Losh { 3166592ffb21SWarner Losh if (property->flags & DRM_MODE_PROP_IMMUTABLE) 3167592ffb21SWarner Losh return false; 3168592ffb21SWarner Losh if (property->flags & DRM_MODE_PROP_RANGE) { 3169592ffb21SWarner Losh if (value < property->values[0] || value > property->values[1]) 3170592ffb21SWarner Losh return false; 3171592ffb21SWarner Losh return true; 3172592ffb21SWarner Losh } else if (property->flags & DRM_MODE_PROP_BITMASK) { 3173592ffb21SWarner Losh int i; 3174592ffb21SWarner Losh uint64_t valid_mask = 0; 3175592ffb21SWarner Losh for (i = 0; i < property->num_values; i++) 3176592ffb21SWarner Losh valid_mask |= (1ULL << property->values[i]); 3177592ffb21SWarner Losh return !(value & ~valid_mask); 3178592ffb21SWarner Losh } else if (property->flags & DRM_MODE_PROP_BLOB) { 3179592ffb21SWarner Losh /* Only the driver knows */ 3180592ffb21SWarner Losh return true; 3181592ffb21SWarner Losh } else { 3182592ffb21SWarner Losh int i; 3183592ffb21SWarner Losh for (i = 0; i < property->num_values; i++) 3184592ffb21SWarner Losh if (property->values[i] == value) 3185592ffb21SWarner Losh return true; 3186592ffb21SWarner Losh return false; 3187592ffb21SWarner Losh } 3188592ffb21SWarner Losh } 3189592ffb21SWarner Losh 3190592ffb21SWarner Losh int drm_mode_connector_property_set_ioctl(struct drm_device *dev, 3191592ffb21SWarner Losh void *data, struct drm_file *file_priv) 3192592ffb21SWarner Losh { 3193592ffb21SWarner Losh struct drm_mode_connector_set_property *conn_set_prop = data; 3194592ffb21SWarner Losh struct drm_mode_obj_set_property obj_set_prop = { 3195592ffb21SWarner Losh .value = conn_set_prop->value, 3196592ffb21SWarner Losh .prop_id = conn_set_prop->prop_id, 3197592ffb21SWarner Losh .obj_id = conn_set_prop->connector_id, 3198592ffb21SWarner Losh .obj_type = DRM_MODE_OBJECT_CONNECTOR 3199592ffb21SWarner Losh }; 3200592ffb21SWarner Losh 3201592ffb21SWarner Losh /* It does all the locking and checking we need */ 3202592ffb21SWarner Losh return drm_mode_obj_set_property_ioctl(dev, &obj_set_prop, file_priv); 3203592ffb21SWarner Losh } 3204592ffb21SWarner Losh 3205592ffb21SWarner Losh static int drm_mode_connector_set_obj_prop(struct drm_mode_object *obj, 3206592ffb21SWarner Losh struct drm_property *property, 3207592ffb21SWarner Losh uint64_t value) 3208592ffb21SWarner Losh { 3209592ffb21SWarner Losh int ret = -EINVAL; 3210592ffb21SWarner Losh struct drm_connector *connector = obj_to_connector(obj); 3211592ffb21SWarner Losh 3212592ffb21SWarner Losh /* Do DPMS ourselves */ 3213592ffb21SWarner Losh if (property == connector->dev->mode_config.dpms_property) { 3214592ffb21SWarner Losh if (connector->funcs->dpms) 3215592ffb21SWarner Losh (*connector->funcs->dpms)(connector, (int)value); 3216592ffb21SWarner Losh ret = 0; 3217592ffb21SWarner Losh } else if (connector->funcs->set_property) 3218592ffb21SWarner Losh ret = connector->funcs->set_property(connector, property, value); 3219592ffb21SWarner Losh 3220592ffb21SWarner Losh /* store the property value if successful */ 3221592ffb21SWarner Losh if (!ret) 3222592ffb21SWarner Losh drm_object_property_set_value(&connector->base, property, value); 3223592ffb21SWarner Losh return ret; 3224592ffb21SWarner Losh } 3225592ffb21SWarner Losh 3226592ffb21SWarner Losh static int drm_mode_crtc_set_obj_prop(struct drm_mode_object *obj, 3227592ffb21SWarner Losh struct drm_property *property, 3228592ffb21SWarner Losh uint64_t value) 3229592ffb21SWarner Losh { 3230592ffb21SWarner Losh int ret = -EINVAL; 3231592ffb21SWarner Losh struct drm_crtc *crtc = obj_to_crtc(obj); 3232592ffb21SWarner Losh 3233592ffb21SWarner Losh if (crtc->funcs->set_property) 3234592ffb21SWarner Losh ret = crtc->funcs->set_property(crtc, property, value); 3235592ffb21SWarner Losh if (!ret) 3236592ffb21SWarner Losh drm_object_property_set_value(obj, property, value); 3237592ffb21SWarner Losh 3238592ffb21SWarner Losh return ret; 3239592ffb21SWarner Losh } 3240592ffb21SWarner Losh 3241592ffb21SWarner Losh static int drm_mode_plane_set_obj_prop(struct drm_mode_object *obj, 3242592ffb21SWarner Losh struct drm_property *property, 3243592ffb21SWarner Losh uint64_t value) 3244592ffb21SWarner Losh { 3245592ffb21SWarner Losh int ret = -EINVAL; 3246592ffb21SWarner Losh struct drm_plane *plane = obj_to_plane(obj); 3247592ffb21SWarner Losh 3248592ffb21SWarner Losh if (plane->funcs->set_property) 3249592ffb21SWarner Losh ret = plane->funcs->set_property(plane, property, value); 3250592ffb21SWarner Losh if (!ret) 3251592ffb21SWarner Losh drm_object_property_set_value(obj, property, value); 3252592ffb21SWarner Losh 3253592ffb21SWarner Losh return ret; 3254592ffb21SWarner Losh } 3255592ffb21SWarner Losh 3256592ffb21SWarner Losh int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data, 3257592ffb21SWarner Losh struct drm_file *file_priv) 3258592ffb21SWarner Losh { 3259592ffb21SWarner Losh struct drm_mode_obj_get_properties *arg = data; 3260592ffb21SWarner Losh struct drm_mode_object *obj; 3261592ffb21SWarner Losh int ret = 0; 3262592ffb21SWarner Losh int i; 3263592ffb21SWarner Losh int copied = 0; 3264592ffb21SWarner Losh int props_count = 0; 3265592ffb21SWarner Losh uint32_t __user *props_ptr; 3266592ffb21SWarner Losh uint64_t __user *prop_values_ptr; 3267592ffb21SWarner Losh 3268592ffb21SWarner Losh if (!drm_core_check_feature(dev, DRIVER_MODESET)) 3269592ffb21SWarner Losh return -EINVAL; 3270592ffb21SWarner Losh 3271592ffb21SWarner Losh sx_xlock(&dev->mode_config.mutex); 3272592ffb21SWarner Losh 3273592ffb21SWarner Losh obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type); 3274592ffb21SWarner Losh if (!obj) { 3275592ffb21SWarner Losh ret = -EINVAL; 3276592ffb21SWarner Losh goto out; 3277592ffb21SWarner Losh } 3278592ffb21SWarner Losh if (!obj->properties) { 3279592ffb21SWarner Losh ret = -EINVAL; 3280592ffb21SWarner Losh goto out; 3281592ffb21SWarner Losh } 3282592ffb21SWarner Losh 3283592ffb21SWarner Losh props_count = obj->properties->count; 3284592ffb21SWarner Losh 3285592ffb21SWarner Losh /* This ioctl is called twice, once to determine how much space is 3286592ffb21SWarner Losh * needed, and the 2nd time to fill it. */ 3287592ffb21SWarner Losh if ((arg->count_props >= props_count) && props_count) { 3288592ffb21SWarner Losh copied = 0; 3289592ffb21SWarner Losh props_ptr = (uint32_t __user *)(unsigned long)(arg->props_ptr); 3290592ffb21SWarner Losh prop_values_ptr = (uint64_t __user *)(unsigned long) 3291592ffb21SWarner Losh (arg->prop_values_ptr); 3292592ffb21SWarner Losh for (i = 0; i < props_count; i++) { 3293592ffb21SWarner Losh if (put_user(obj->properties->ids[i], 3294592ffb21SWarner Losh props_ptr + copied)) { 3295592ffb21SWarner Losh ret = -EFAULT; 3296592ffb21SWarner Losh goto out; 3297592ffb21SWarner Losh } 3298592ffb21SWarner Losh if (put_user(obj->properties->values[i], 3299592ffb21SWarner Losh prop_values_ptr + copied)) { 3300592ffb21SWarner Losh ret = -EFAULT; 3301592ffb21SWarner Losh goto out; 3302592ffb21SWarner Losh } 3303592ffb21SWarner Losh copied++; 3304592ffb21SWarner Losh } 3305592ffb21SWarner Losh } 3306592ffb21SWarner Losh arg->count_props = props_count; 3307592ffb21SWarner Losh out: 3308592ffb21SWarner Losh sx_xunlock(&dev->mode_config.mutex); 3309592ffb21SWarner Losh return ret; 3310592ffb21SWarner Losh } 3311592ffb21SWarner Losh 3312592ffb21SWarner Losh int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data, 3313592ffb21SWarner Losh struct drm_file *file_priv) 3314592ffb21SWarner Losh { 3315592ffb21SWarner Losh struct drm_mode_obj_set_property *arg = data; 3316592ffb21SWarner Losh struct drm_mode_object *arg_obj; 3317592ffb21SWarner Losh struct drm_mode_object *prop_obj; 3318592ffb21SWarner Losh struct drm_property *property; 3319592ffb21SWarner Losh int ret = -EINVAL; 3320592ffb21SWarner Losh int i; 3321592ffb21SWarner Losh 3322592ffb21SWarner Losh if (!drm_core_check_feature(dev, DRIVER_MODESET)) 3323592ffb21SWarner Losh return -EINVAL; 3324592ffb21SWarner Losh 3325592ffb21SWarner Losh sx_xlock(&dev->mode_config.mutex); 3326592ffb21SWarner Losh 3327592ffb21SWarner Losh arg_obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type); 3328592ffb21SWarner Losh if (!arg_obj) 3329592ffb21SWarner Losh goto out; 3330592ffb21SWarner Losh if (!arg_obj->properties) 3331592ffb21SWarner Losh goto out; 3332592ffb21SWarner Losh 3333592ffb21SWarner Losh for (i = 0; i < arg_obj->properties->count; i++) 3334592ffb21SWarner Losh if (arg_obj->properties->ids[i] == arg->prop_id) 3335592ffb21SWarner Losh break; 3336592ffb21SWarner Losh 3337592ffb21SWarner Losh if (i == arg_obj->properties->count) 3338592ffb21SWarner Losh goto out; 3339592ffb21SWarner Losh 3340592ffb21SWarner Losh prop_obj = drm_mode_object_find(dev, arg->prop_id, 3341592ffb21SWarner Losh DRM_MODE_OBJECT_PROPERTY); 3342592ffb21SWarner Losh if (!prop_obj) 3343592ffb21SWarner Losh goto out; 3344592ffb21SWarner Losh property = obj_to_property(prop_obj); 3345592ffb21SWarner Losh 3346592ffb21SWarner Losh if (!drm_property_change_is_valid(property, arg->value)) 3347592ffb21SWarner Losh goto out; 3348592ffb21SWarner Losh 3349592ffb21SWarner Losh switch (arg_obj->type) { 3350592ffb21SWarner Losh case DRM_MODE_OBJECT_CONNECTOR: 3351592ffb21SWarner Losh ret = drm_mode_connector_set_obj_prop(arg_obj, property, 3352592ffb21SWarner Losh arg->value); 3353592ffb21SWarner Losh break; 3354592ffb21SWarner Losh case DRM_MODE_OBJECT_CRTC: 3355592ffb21SWarner Losh ret = drm_mode_crtc_set_obj_prop(arg_obj, property, arg->value); 3356592ffb21SWarner Losh break; 3357592ffb21SWarner Losh case DRM_MODE_OBJECT_PLANE: 3358592ffb21SWarner Losh ret = drm_mode_plane_set_obj_prop(arg_obj, property, arg->value); 3359592ffb21SWarner Losh break; 3360592ffb21SWarner Losh } 3361592ffb21SWarner Losh 3362592ffb21SWarner Losh out: 3363592ffb21SWarner Losh sx_xunlock(&dev->mode_config.mutex); 3364592ffb21SWarner Losh return ret; 3365592ffb21SWarner Losh } 3366592ffb21SWarner Losh 3367592ffb21SWarner Losh int drm_mode_connector_attach_encoder(struct drm_connector *connector, 3368592ffb21SWarner Losh struct drm_encoder *encoder) 3369592ffb21SWarner Losh { 3370592ffb21SWarner Losh int i; 3371592ffb21SWarner Losh 3372592ffb21SWarner Losh for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { 3373592ffb21SWarner Losh if (connector->encoder_ids[i] == 0) { 3374592ffb21SWarner Losh connector->encoder_ids[i] = encoder->base.id; 3375592ffb21SWarner Losh return 0; 3376592ffb21SWarner Losh } 3377592ffb21SWarner Losh } 3378592ffb21SWarner Losh return -ENOMEM; 3379592ffb21SWarner Losh } 3380592ffb21SWarner Losh EXPORT_SYMBOL(drm_mode_connector_attach_encoder); 3381592ffb21SWarner Losh 3382592ffb21SWarner Losh void drm_mode_connector_detach_encoder(struct drm_connector *connector, 3383592ffb21SWarner Losh struct drm_encoder *encoder) 3384592ffb21SWarner Losh { 3385592ffb21SWarner Losh int i; 3386592ffb21SWarner Losh for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { 3387592ffb21SWarner Losh if (connector->encoder_ids[i] == encoder->base.id) { 3388592ffb21SWarner Losh connector->encoder_ids[i] = 0; 3389592ffb21SWarner Losh if (connector->encoder == encoder) 3390592ffb21SWarner Losh connector->encoder = NULL; 3391592ffb21SWarner Losh break; 3392592ffb21SWarner Losh } 3393592ffb21SWarner Losh } 3394592ffb21SWarner Losh } 3395592ffb21SWarner Losh EXPORT_SYMBOL(drm_mode_connector_detach_encoder); 3396592ffb21SWarner Losh 3397592ffb21SWarner Losh int drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc, 3398592ffb21SWarner Losh int gamma_size) 3399592ffb21SWarner Losh { 3400592ffb21SWarner Losh crtc->gamma_size = gamma_size; 3401592ffb21SWarner Losh 3402592ffb21SWarner Losh crtc->gamma_store = malloc(gamma_size * sizeof(uint16_t) * 3, 3403592ffb21SWarner Losh DRM_MEM_KMS, M_WAITOK | M_ZERO); 3404592ffb21SWarner Losh 3405592ffb21SWarner Losh return 0; 3406592ffb21SWarner Losh } 3407592ffb21SWarner Losh EXPORT_SYMBOL(drm_mode_crtc_set_gamma_size); 3408592ffb21SWarner Losh 3409592ffb21SWarner Losh int drm_mode_gamma_set_ioctl(struct drm_device *dev, 3410592ffb21SWarner Losh void *data, struct drm_file *file_priv) 3411592ffb21SWarner Losh { 3412592ffb21SWarner Losh struct drm_mode_crtc_lut *crtc_lut = data; 3413592ffb21SWarner Losh struct drm_mode_object *obj; 3414592ffb21SWarner Losh struct drm_crtc *crtc; 3415592ffb21SWarner Losh void *r_base, *g_base, *b_base; 3416592ffb21SWarner Losh int size; 3417592ffb21SWarner Losh int ret = 0; 3418592ffb21SWarner Losh 3419592ffb21SWarner Losh if (!drm_core_check_feature(dev, DRIVER_MODESET)) 3420592ffb21SWarner Losh return -EINVAL; 3421592ffb21SWarner Losh 3422592ffb21SWarner Losh sx_xlock(&dev->mode_config.mutex); 3423592ffb21SWarner Losh obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC); 3424592ffb21SWarner Losh if (!obj) { 3425592ffb21SWarner Losh ret = -EINVAL; 3426592ffb21SWarner Losh goto out; 3427592ffb21SWarner Losh } 3428592ffb21SWarner Losh crtc = obj_to_crtc(obj); 3429592ffb21SWarner Losh 3430592ffb21SWarner Losh if (crtc->funcs->gamma_set == NULL) { 3431592ffb21SWarner Losh ret = -ENOSYS; 3432592ffb21SWarner Losh goto out; 3433592ffb21SWarner Losh } 3434592ffb21SWarner Losh 3435592ffb21SWarner Losh /* memcpy into gamma store */ 3436592ffb21SWarner Losh if (crtc_lut->gamma_size != crtc->gamma_size) { 3437592ffb21SWarner Losh ret = -EINVAL; 3438592ffb21SWarner Losh goto out; 3439592ffb21SWarner Losh } 3440592ffb21SWarner Losh 3441592ffb21SWarner Losh size = crtc_lut->gamma_size * (sizeof(uint16_t)); 3442592ffb21SWarner Losh r_base = crtc->gamma_store; 3443592ffb21SWarner Losh if (copy_from_user(r_base, (void __user *)(unsigned long)crtc_lut->red, size)) { 3444592ffb21SWarner Losh ret = -EFAULT; 3445592ffb21SWarner Losh goto out; 3446592ffb21SWarner Losh } 3447592ffb21SWarner Losh 3448592ffb21SWarner Losh g_base = (char *)r_base + size; 3449592ffb21SWarner Losh if (copy_from_user(g_base, (void __user *)(unsigned long)crtc_lut->green, size)) { 3450592ffb21SWarner Losh ret = -EFAULT; 3451592ffb21SWarner Losh goto out; 3452592ffb21SWarner Losh } 3453592ffb21SWarner Losh 3454592ffb21SWarner Losh b_base = (char *)g_base + size; 3455592ffb21SWarner Losh if (copy_from_user(b_base, (void __user *)(unsigned long)crtc_lut->blue, size)) { 3456592ffb21SWarner Losh ret = -EFAULT; 3457592ffb21SWarner Losh goto out; 3458592ffb21SWarner Losh } 3459592ffb21SWarner Losh 3460592ffb21SWarner Losh crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, 0, crtc->gamma_size); 3461592ffb21SWarner Losh 3462592ffb21SWarner Losh out: 3463592ffb21SWarner Losh sx_xunlock(&dev->mode_config.mutex); 3464592ffb21SWarner Losh return ret; 3465592ffb21SWarner Losh 3466592ffb21SWarner Losh } 3467592ffb21SWarner Losh 3468592ffb21SWarner Losh int drm_mode_gamma_get_ioctl(struct drm_device *dev, 3469592ffb21SWarner Losh void *data, struct drm_file *file_priv) 3470592ffb21SWarner Losh { 3471592ffb21SWarner Losh struct drm_mode_crtc_lut *crtc_lut = data; 3472592ffb21SWarner Losh struct drm_mode_object *obj; 3473592ffb21SWarner Losh struct drm_crtc *crtc; 3474592ffb21SWarner Losh void *r_base, *g_base, *b_base; 3475592ffb21SWarner Losh int size; 3476592ffb21SWarner Losh int ret = 0; 3477592ffb21SWarner Losh 3478592ffb21SWarner Losh if (!drm_core_check_feature(dev, DRIVER_MODESET)) 3479592ffb21SWarner Losh return -EINVAL; 3480592ffb21SWarner Losh 3481592ffb21SWarner Losh sx_xlock(&dev->mode_config.mutex); 3482592ffb21SWarner Losh obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC); 3483592ffb21SWarner Losh if (!obj) { 3484592ffb21SWarner Losh ret = -EINVAL; 3485592ffb21SWarner Losh goto out; 3486592ffb21SWarner Losh } 3487592ffb21SWarner Losh crtc = obj_to_crtc(obj); 3488592ffb21SWarner Losh 3489592ffb21SWarner Losh /* memcpy into gamma store */ 3490592ffb21SWarner Losh if (crtc_lut->gamma_size != crtc->gamma_size) { 3491592ffb21SWarner Losh ret = -EINVAL; 3492592ffb21SWarner Losh goto out; 3493592ffb21SWarner Losh } 3494592ffb21SWarner Losh 3495592ffb21SWarner Losh size = crtc_lut->gamma_size * (sizeof(uint16_t)); 3496592ffb21SWarner Losh r_base = crtc->gamma_store; 3497592ffb21SWarner Losh if (copy_to_user((void __user *)(unsigned long)crtc_lut->red, r_base, size)) { 3498592ffb21SWarner Losh ret = -EFAULT; 3499592ffb21SWarner Losh goto out; 3500592ffb21SWarner Losh } 3501592ffb21SWarner Losh 3502592ffb21SWarner Losh g_base = (char *)r_base + size; 3503592ffb21SWarner Losh if (copy_to_user((void __user *)(unsigned long)crtc_lut->green, g_base, size)) { 3504592ffb21SWarner Losh ret = -EFAULT; 3505592ffb21SWarner Losh goto out; 3506592ffb21SWarner Losh } 3507592ffb21SWarner Losh 3508592ffb21SWarner Losh b_base = (char *)g_base + size; 3509592ffb21SWarner Losh if (copy_to_user((void __user *)(unsigned long)crtc_lut->blue, b_base, size)) { 3510592ffb21SWarner Losh ret = -EFAULT; 3511592ffb21SWarner Losh goto out; 3512592ffb21SWarner Losh } 3513592ffb21SWarner Losh out: 3514592ffb21SWarner Losh sx_xunlock(&dev->mode_config.mutex); 3515592ffb21SWarner Losh return ret; 3516592ffb21SWarner Losh } 3517592ffb21SWarner Losh 3518592ffb21SWarner Losh static void 3519592ffb21SWarner Losh free_vblank_event(void *arg) 3520592ffb21SWarner Losh { 3521592ffb21SWarner Losh 3522592ffb21SWarner Losh free(arg, DRM_MEM_KMS); 3523592ffb21SWarner Losh } 3524592ffb21SWarner Losh 3525592ffb21SWarner Losh int drm_mode_page_flip_ioctl(struct drm_device *dev, 3526592ffb21SWarner Losh void *data, struct drm_file *file_priv) 3527592ffb21SWarner Losh { 3528592ffb21SWarner Losh struct drm_mode_crtc_page_flip *page_flip = data; 3529592ffb21SWarner Losh struct drm_mode_object *obj; 3530592ffb21SWarner Losh struct drm_crtc *crtc; 3531592ffb21SWarner Losh struct drm_framebuffer *fb; 3532592ffb21SWarner Losh struct drm_pending_vblank_event *e = NULL; 3533592ffb21SWarner Losh #ifdef __linux__ 3534592ffb21SWarner Losh unsigned long flags; 3535592ffb21SWarner Losh #endif 3536592ffb21SWarner Losh int hdisplay, vdisplay; 3537592ffb21SWarner Losh int ret = -EINVAL; 3538592ffb21SWarner Losh 3539592ffb21SWarner Losh if (page_flip->flags & ~DRM_MODE_PAGE_FLIP_FLAGS || 3540592ffb21SWarner Losh page_flip->reserved != 0) 3541592ffb21SWarner Losh return -EINVAL; 3542592ffb21SWarner Losh 3543592ffb21SWarner Losh sx_xlock(&dev->mode_config.mutex); 3544592ffb21SWarner Losh obj = drm_mode_object_find(dev, page_flip->crtc_id, DRM_MODE_OBJECT_CRTC); 3545592ffb21SWarner Losh if (!obj) 3546592ffb21SWarner Losh goto out; 3547592ffb21SWarner Losh crtc = obj_to_crtc(obj); 3548592ffb21SWarner Losh 3549592ffb21SWarner Losh if (crtc->fb == NULL) { 3550592ffb21SWarner Losh /* The framebuffer is currently unbound, presumably 3551592ffb21SWarner Losh * due to a hotplug event, that userspace has not 3552592ffb21SWarner Losh * yet discovered. 3553592ffb21SWarner Losh */ 3554592ffb21SWarner Losh ret = -EBUSY; 3555592ffb21SWarner Losh goto out; 3556592ffb21SWarner Losh } 3557592ffb21SWarner Losh 3558592ffb21SWarner Losh if (crtc->funcs->page_flip == NULL) 3559592ffb21SWarner Losh goto out; 3560592ffb21SWarner Losh 3561592ffb21SWarner Losh obj = drm_mode_object_find(dev, page_flip->fb_id, DRM_MODE_OBJECT_FB); 3562592ffb21SWarner Losh if (!obj) 3563592ffb21SWarner Losh goto out; 3564592ffb21SWarner Losh fb = obj_to_fb(obj); 3565592ffb21SWarner Losh 3566592ffb21SWarner Losh hdisplay = crtc->mode.hdisplay; 3567592ffb21SWarner Losh vdisplay = crtc->mode.vdisplay; 3568592ffb21SWarner Losh 3569592ffb21SWarner Losh if (crtc->invert_dimensions) { 3570592ffb21SWarner Losh int tmp; 3571592ffb21SWarner Losh tmp = vdisplay; 3572592ffb21SWarner Losh vdisplay = hdisplay; 3573592ffb21SWarner Losh hdisplay = tmp; 3574592ffb21SWarner Losh } 3575592ffb21SWarner Losh 3576592ffb21SWarner Losh if (hdisplay > fb->width || 3577592ffb21SWarner Losh vdisplay > fb->height || 3578592ffb21SWarner Losh crtc->x > fb->width - hdisplay || 3579592ffb21SWarner Losh crtc->y > fb->height - vdisplay) { 3580592ffb21SWarner Losh DRM_DEBUG_KMS("Invalid fb size %ux%u for CRTC viewport %ux%u+%d+%d%s.\n", 3581592ffb21SWarner Losh fb->width, fb->height, hdisplay, vdisplay, crtc->x, crtc->y, 3582592ffb21SWarner Losh crtc->invert_dimensions ? " (inverted)" : ""); 3583592ffb21SWarner Losh ret = -ENOSPC; 3584592ffb21SWarner Losh goto out; 3585592ffb21SWarner Losh } 3586592ffb21SWarner Losh 3587592ffb21SWarner Losh if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) { 3588592ffb21SWarner Losh ret = -ENOMEM; 3589592ffb21SWarner Losh mtx_lock(&dev->event_lock); 3590592ffb21SWarner Losh if (file_priv->event_space < sizeof e->event) { 3591592ffb21SWarner Losh mtx_unlock(&dev->event_lock); 3592592ffb21SWarner Losh goto out; 3593592ffb21SWarner Losh } 3594592ffb21SWarner Losh file_priv->event_space -= sizeof e->event; 3595592ffb21SWarner Losh mtx_unlock(&dev->event_lock); 3596592ffb21SWarner Losh 3597592ffb21SWarner Losh e = malloc(sizeof *e, DRM_MEM_KMS, M_WAITOK | M_ZERO); 3598592ffb21SWarner Losh e->event.base.type = DRM_EVENT_FLIP_COMPLETE; 3599592ffb21SWarner Losh e->event.base.length = sizeof e->event; 3600592ffb21SWarner Losh e->event.user_data = page_flip->user_data; 3601592ffb21SWarner Losh e->base.event = &e->event.base; 3602592ffb21SWarner Losh e->base.file_priv = file_priv; 3603592ffb21SWarner Losh e->base.destroy = 3604592ffb21SWarner Losh (void (*) (struct drm_pending_event *)) free_vblank_event; 3605592ffb21SWarner Losh } 3606592ffb21SWarner Losh 3607592ffb21SWarner Losh ret = crtc->funcs->page_flip(crtc, fb, e); 3608592ffb21SWarner Losh if (ret) { 3609592ffb21SWarner Losh if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) { 3610592ffb21SWarner Losh mtx_lock(&dev->event_lock); 3611592ffb21SWarner Losh file_priv->event_space += sizeof e->event; 3612592ffb21SWarner Losh mtx_unlock(&dev->event_lock); 3613592ffb21SWarner Losh free(e, DRM_MEM_KMS); 3614592ffb21SWarner Losh } 3615592ffb21SWarner Losh } 3616592ffb21SWarner Losh 3617592ffb21SWarner Losh out: 3618592ffb21SWarner Losh sx_xunlock(&dev->mode_config.mutex); 3619592ffb21SWarner Losh return ret; 3620592ffb21SWarner Losh } 3621592ffb21SWarner Losh 3622592ffb21SWarner Losh void drm_mode_config_reset(struct drm_device *dev) 3623592ffb21SWarner Losh { 3624592ffb21SWarner Losh struct drm_crtc *crtc; 3625592ffb21SWarner Losh struct drm_encoder *encoder; 3626592ffb21SWarner Losh struct drm_connector *connector; 3627592ffb21SWarner Losh 3628592ffb21SWarner Losh list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) 3629592ffb21SWarner Losh if (crtc->funcs->reset) 3630592ffb21SWarner Losh crtc->funcs->reset(crtc); 3631592ffb21SWarner Losh 3632592ffb21SWarner Losh list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) 3633592ffb21SWarner Losh if (encoder->funcs->reset) 3634592ffb21SWarner Losh encoder->funcs->reset(encoder); 3635592ffb21SWarner Losh 3636592ffb21SWarner Losh list_for_each_entry(connector, &dev->mode_config.connector_list, head) { 3637592ffb21SWarner Losh connector->status = connector_status_unknown; 3638592ffb21SWarner Losh 3639592ffb21SWarner Losh if (connector->funcs->reset) 3640592ffb21SWarner Losh connector->funcs->reset(connector); 3641592ffb21SWarner Losh } 3642592ffb21SWarner Losh } 3643592ffb21SWarner Losh EXPORT_SYMBOL(drm_mode_config_reset); 3644592ffb21SWarner Losh 3645592ffb21SWarner Losh int drm_mode_create_dumb_ioctl(struct drm_device *dev, 3646592ffb21SWarner Losh void *data, struct drm_file *file_priv) 3647592ffb21SWarner Losh { 3648592ffb21SWarner Losh struct drm_mode_create_dumb *args = data; 3649592ffb21SWarner Losh 3650592ffb21SWarner Losh if (!dev->driver->dumb_create) 3651592ffb21SWarner Losh return -ENOSYS; 3652592ffb21SWarner Losh return dev->driver->dumb_create(file_priv, dev, args); 3653592ffb21SWarner Losh } 3654592ffb21SWarner Losh 3655592ffb21SWarner Losh int drm_mode_mmap_dumb_ioctl(struct drm_device *dev, 3656592ffb21SWarner Losh void *data, struct drm_file *file_priv) 3657592ffb21SWarner Losh { 3658592ffb21SWarner Losh struct drm_mode_map_dumb *args = data; 3659592ffb21SWarner Losh 3660592ffb21SWarner Losh /* call driver ioctl to get mmap offset */ 3661592ffb21SWarner Losh if (!dev->driver->dumb_map_offset) 3662592ffb21SWarner Losh return -ENOSYS; 3663592ffb21SWarner Losh 3664592ffb21SWarner Losh return dev->driver->dumb_map_offset(file_priv, dev, args->handle, &args->offset); 3665592ffb21SWarner Losh } 3666592ffb21SWarner Losh 3667592ffb21SWarner Losh int drm_mode_destroy_dumb_ioctl(struct drm_device *dev, 3668592ffb21SWarner Losh void *data, struct drm_file *file_priv) 3669592ffb21SWarner Losh { 3670592ffb21SWarner Losh struct drm_mode_destroy_dumb *args = data; 3671592ffb21SWarner Losh 3672592ffb21SWarner Losh if (!dev->driver->dumb_destroy) 3673592ffb21SWarner Losh return -ENOSYS; 3674592ffb21SWarner Losh 3675592ffb21SWarner Losh return dev->driver->dumb_destroy(file_priv, dev, args->handle); 3676592ffb21SWarner Losh } 3677592ffb21SWarner Losh 3678592ffb21SWarner Losh /* 3679592ffb21SWarner Losh * Just need to support RGB formats here for compat with code that doesn't 3680592ffb21SWarner Losh * use pixel formats directly yet. 3681592ffb21SWarner Losh */ 3682592ffb21SWarner Losh void drm_fb_get_bpp_depth(uint32_t format, unsigned int *depth, 3683592ffb21SWarner Losh int *bpp) 3684592ffb21SWarner Losh { 3685592ffb21SWarner Losh switch (format) { 3686592ffb21SWarner Losh case DRM_FORMAT_C8: 3687592ffb21SWarner Losh case DRM_FORMAT_RGB332: 3688592ffb21SWarner Losh case DRM_FORMAT_BGR233: 3689592ffb21SWarner Losh *depth = 8; 3690592ffb21SWarner Losh *bpp = 8; 3691592ffb21SWarner Losh break; 3692592ffb21SWarner Losh case DRM_FORMAT_XRGB1555: 3693592ffb21SWarner Losh case DRM_FORMAT_XBGR1555: 3694592ffb21SWarner Losh case DRM_FORMAT_RGBX5551: 3695592ffb21SWarner Losh case DRM_FORMAT_BGRX5551: 3696592ffb21SWarner Losh case DRM_FORMAT_ARGB1555: 3697592ffb21SWarner Losh case DRM_FORMAT_ABGR1555: 3698592ffb21SWarner Losh case DRM_FORMAT_RGBA5551: 3699592ffb21SWarner Losh case DRM_FORMAT_BGRA5551: 3700592ffb21SWarner Losh *depth = 15; 3701592ffb21SWarner Losh *bpp = 16; 3702592ffb21SWarner Losh break; 3703592ffb21SWarner Losh case DRM_FORMAT_RGB565: 3704592ffb21SWarner Losh case DRM_FORMAT_BGR565: 3705592ffb21SWarner Losh *depth = 16; 3706592ffb21SWarner Losh *bpp = 16; 3707592ffb21SWarner Losh break; 3708592ffb21SWarner Losh case DRM_FORMAT_RGB888: 3709592ffb21SWarner Losh case DRM_FORMAT_BGR888: 3710592ffb21SWarner Losh *depth = 24; 3711592ffb21SWarner Losh *bpp = 24; 3712592ffb21SWarner Losh break; 3713592ffb21SWarner Losh case DRM_FORMAT_XRGB8888: 3714592ffb21SWarner Losh case DRM_FORMAT_XBGR8888: 3715592ffb21SWarner Losh case DRM_FORMAT_RGBX8888: 3716592ffb21SWarner Losh case DRM_FORMAT_BGRX8888: 3717592ffb21SWarner Losh *depth = 24; 3718592ffb21SWarner Losh *bpp = 32; 3719592ffb21SWarner Losh break; 3720592ffb21SWarner Losh case DRM_FORMAT_XRGB2101010: 3721592ffb21SWarner Losh case DRM_FORMAT_XBGR2101010: 3722592ffb21SWarner Losh case DRM_FORMAT_RGBX1010102: 3723592ffb21SWarner Losh case DRM_FORMAT_BGRX1010102: 3724592ffb21SWarner Losh case DRM_FORMAT_ARGB2101010: 3725592ffb21SWarner Losh case DRM_FORMAT_ABGR2101010: 3726592ffb21SWarner Losh case DRM_FORMAT_RGBA1010102: 3727592ffb21SWarner Losh case DRM_FORMAT_BGRA1010102: 3728592ffb21SWarner Losh *depth = 30; 3729592ffb21SWarner Losh *bpp = 32; 3730592ffb21SWarner Losh break; 3731592ffb21SWarner Losh case DRM_FORMAT_ARGB8888: 3732592ffb21SWarner Losh case DRM_FORMAT_ABGR8888: 3733592ffb21SWarner Losh case DRM_FORMAT_RGBA8888: 3734592ffb21SWarner Losh case DRM_FORMAT_BGRA8888: 3735592ffb21SWarner Losh *depth = 32; 3736592ffb21SWarner Losh *bpp = 32; 3737592ffb21SWarner Losh break; 3738592ffb21SWarner Losh default: 3739592ffb21SWarner Losh DRM_DEBUG_KMS("unsupported pixel format\n"); 3740592ffb21SWarner Losh *depth = 0; 3741592ffb21SWarner Losh *bpp = 0; 3742592ffb21SWarner Losh break; 3743592ffb21SWarner Losh } 3744592ffb21SWarner Losh } 3745592ffb21SWarner Losh EXPORT_SYMBOL(drm_fb_get_bpp_depth); 3746592ffb21SWarner Losh 3747592ffb21SWarner Losh /** 3748592ffb21SWarner Losh * drm_format_num_planes - get the number of planes for format 3749592ffb21SWarner Losh * @format: pixel format (DRM_FORMAT_*) 3750592ffb21SWarner Losh * 3751592ffb21SWarner Losh * RETURNS: 3752592ffb21SWarner Losh * The number of planes used by the specified pixel format. 3753592ffb21SWarner Losh */ 3754592ffb21SWarner Losh int drm_format_num_planes(uint32_t format) 3755592ffb21SWarner Losh { 3756592ffb21SWarner Losh switch (format) { 3757592ffb21SWarner Losh case DRM_FORMAT_YUV410: 3758592ffb21SWarner Losh case DRM_FORMAT_YVU410: 3759592ffb21SWarner Losh case DRM_FORMAT_YUV411: 3760592ffb21SWarner Losh case DRM_FORMAT_YVU411: 3761592ffb21SWarner Losh case DRM_FORMAT_YUV420: 3762592ffb21SWarner Losh case DRM_FORMAT_YVU420: 3763592ffb21SWarner Losh case DRM_FORMAT_YUV422: 3764592ffb21SWarner Losh case DRM_FORMAT_YVU422: 3765592ffb21SWarner Losh case DRM_FORMAT_YUV444: 3766592ffb21SWarner Losh case DRM_FORMAT_YVU444: 3767592ffb21SWarner Losh return 3; 3768592ffb21SWarner Losh case DRM_FORMAT_NV12: 3769592ffb21SWarner Losh case DRM_FORMAT_NV21: 3770592ffb21SWarner Losh case DRM_FORMAT_NV16: 3771592ffb21SWarner Losh case DRM_FORMAT_NV61: 3772592ffb21SWarner Losh case DRM_FORMAT_NV24: 3773592ffb21SWarner Losh case DRM_FORMAT_NV42: 3774592ffb21SWarner Losh return 2; 3775592ffb21SWarner Losh default: 3776592ffb21SWarner Losh return 1; 3777592ffb21SWarner Losh } 3778592ffb21SWarner Losh } 3779592ffb21SWarner Losh EXPORT_SYMBOL(drm_format_num_planes); 3780592ffb21SWarner Losh 3781592ffb21SWarner Losh /** 3782592ffb21SWarner Losh * drm_format_plane_cpp - determine the bytes per pixel value 3783592ffb21SWarner Losh * @format: pixel format (DRM_FORMAT_*) 3784592ffb21SWarner Losh * @plane: plane index 3785592ffb21SWarner Losh * 3786592ffb21SWarner Losh * RETURNS: 3787592ffb21SWarner Losh * The bytes per pixel value for the specified plane. 3788592ffb21SWarner Losh */ 3789592ffb21SWarner Losh int drm_format_plane_cpp(uint32_t format, int plane) 3790592ffb21SWarner Losh { 3791592ffb21SWarner Losh unsigned int depth; 3792592ffb21SWarner Losh int bpp; 3793592ffb21SWarner Losh 3794592ffb21SWarner Losh if (plane >= drm_format_num_planes(format)) 3795592ffb21SWarner Losh return 0; 3796592ffb21SWarner Losh 3797592ffb21SWarner Losh switch (format) { 3798592ffb21SWarner Losh case DRM_FORMAT_YUYV: 3799592ffb21SWarner Losh case DRM_FORMAT_YVYU: 3800592ffb21SWarner Losh case DRM_FORMAT_UYVY: 3801592ffb21SWarner Losh case DRM_FORMAT_VYUY: 3802592ffb21SWarner Losh return 2; 3803592ffb21SWarner Losh case DRM_FORMAT_NV12: 3804592ffb21SWarner Losh case DRM_FORMAT_NV21: 3805592ffb21SWarner Losh case DRM_FORMAT_NV16: 3806592ffb21SWarner Losh case DRM_FORMAT_NV61: 3807592ffb21SWarner Losh case DRM_FORMAT_NV24: 3808592ffb21SWarner Losh case DRM_FORMAT_NV42: 3809592ffb21SWarner Losh return plane ? 2 : 1; 3810592ffb21SWarner Losh case DRM_FORMAT_YUV410: 3811592ffb21SWarner Losh case DRM_FORMAT_YVU410: 3812592ffb21SWarner Losh case DRM_FORMAT_YUV411: 3813592ffb21SWarner Losh case DRM_FORMAT_YVU411: 3814592ffb21SWarner Losh case DRM_FORMAT_YUV420: 3815592ffb21SWarner Losh case DRM_FORMAT_YVU420: 3816592ffb21SWarner Losh case DRM_FORMAT_YUV422: 3817592ffb21SWarner Losh case DRM_FORMAT_YVU422: 3818592ffb21SWarner Losh case DRM_FORMAT_YUV444: 3819592ffb21SWarner Losh case DRM_FORMAT_YVU444: 3820592ffb21SWarner Losh return 1; 3821592ffb21SWarner Losh default: 3822592ffb21SWarner Losh drm_fb_get_bpp_depth(format, &depth, &bpp); 3823592ffb21SWarner Losh return bpp >> 3; 3824592ffb21SWarner Losh } 3825592ffb21SWarner Losh } 3826592ffb21SWarner Losh EXPORT_SYMBOL(drm_format_plane_cpp); 3827592ffb21SWarner Losh 3828592ffb21SWarner Losh /** 3829592ffb21SWarner Losh * drm_format_horz_chroma_subsampling - get the horizontal chroma subsampling factor 3830592ffb21SWarner Losh * @format: pixel format (DRM_FORMAT_*) 3831592ffb21SWarner Losh * 3832592ffb21SWarner Losh * RETURNS: 3833592ffb21SWarner Losh * The horizontal chroma subsampling factor for the 3834592ffb21SWarner Losh * specified pixel format. 3835592ffb21SWarner Losh */ 3836592ffb21SWarner Losh int drm_format_horz_chroma_subsampling(uint32_t format) 3837592ffb21SWarner Losh { 3838592ffb21SWarner Losh switch (format) { 3839592ffb21SWarner Losh case DRM_FORMAT_YUV411: 3840592ffb21SWarner Losh case DRM_FORMAT_YVU411: 3841592ffb21SWarner Losh case DRM_FORMAT_YUV410: 3842592ffb21SWarner Losh case DRM_FORMAT_YVU410: 3843592ffb21SWarner Losh return 4; 3844592ffb21SWarner Losh case DRM_FORMAT_YUYV: 3845592ffb21SWarner Losh case DRM_FORMAT_YVYU: 3846592ffb21SWarner Losh case DRM_FORMAT_UYVY: 3847592ffb21SWarner Losh case DRM_FORMAT_VYUY: 3848592ffb21SWarner Losh case DRM_FORMAT_NV12: 3849592ffb21SWarner Losh case DRM_FORMAT_NV21: 3850592ffb21SWarner Losh case DRM_FORMAT_NV16: 3851592ffb21SWarner Losh case DRM_FORMAT_NV61: 3852592ffb21SWarner Losh case DRM_FORMAT_YUV422: 3853592ffb21SWarner Losh case DRM_FORMAT_YVU422: 3854592ffb21SWarner Losh case DRM_FORMAT_YUV420: 3855592ffb21SWarner Losh case DRM_FORMAT_YVU420: 3856592ffb21SWarner Losh return 2; 3857592ffb21SWarner Losh default: 3858592ffb21SWarner Losh return 1; 3859592ffb21SWarner Losh } 3860592ffb21SWarner Losh } 3861592ffb21SWarner Losh EXPORT_SYMBOL(drm_format_horz_chroma_subsampling); 3862592ffb21SWarner Losh 3863592ffb21SWarner Losh /** 3864592ffb21SWarner Losh * drm_format_vert_chroma_subsampling - get the vertical chroma subsampling factor 3865592ffb21SWarner Losh * @format: pixel format (DRM_FORMAT_*) 3866592ffb21SWarner Losh * 3867592ffb21SWarner Losh * RETURNS: 3868592ffb21SWarner Losh * The vertical chroma subsampling factor for the 3869592ffb21SWarner Losh * specified pixel format. 3870592ffb21SWarner Losh */ 3871592ffb21SWarner Losh int drm_format_vert_chroma_subsampling(uint32_t format) 3872592ffb21SWarner Losh { 3873592ffb21SWarner Losh switch (format) { 3874592ffb21SWarner Losh case DRM_FORMAT_YUV410: 3875592ffb21SWarner Losh case DRM_FORMAT_YVU410: 3876592ffb21SWarner Losh return 4; 3877592ffb21SWarner Losh case DRM_FORMAT_YUV420: 3878592ffb21SWarner Losh case DRM_FORMAT_YVU420: 3879592ffb21SWarner Losh case DRM_FORMAT_NV12: 3880592ffb21SWarner Losh case DRM_FORMAT_NV21: 3881592ffb21SWarner Losh return 2; 3882592ffb21SWarner Losh default: 3883592ffb21SWarner Losh return 1; 3884592ffb21SWarner Losh } 3885592ffb21SWarner Losh } 3886592ffb21SWarner Losh EXPORT_SYMBOL(drm_format_vert_chroma_subsampling); 3887