1235783Skib/* 2235783Skib * Copyright (c) 2006-2008 Intel Corporation 3235783Skib * Copyright (c) 2007 Dave Airlie <airlied@linux.ie> 4235783Skib * Copyright (c) 2008 Red Hat Inc. 5235783Skib * 6235783Skib * DRM core CRTC related functions 7235783Skib * 8235783Skib * Permission to use, copy, modify, distribute, and sell this software and its 9235783Skib * documentation for any purpose is hereby granted without fee, provided that 10235783Skib * the above copyright notice appear in all copies and that both that copyright 11235783Skib * notice and this permission notice appear in supporting documentation, and 12235783Skib * that the name of the copyright holders not be used in advertising or 13235783Skib * publicity pertaining to distribution of the software without specific, 14235783Skib * written prior permission. The copyright holders make no representations 15235783Skib * about the suitability of this software for any purpose. It is provided "as 16235783Skib * is" without express or implied warranty. 17235783Skib * 18235783Skib * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 19235783Skib * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 20235783Skib * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR 21235783Skib * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 22235783Skib * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 23235783Skib * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 24235783Skib * OF THIS SOFTWARE. 25235783Skib * 26235783Skib * Authors: 27235783Skib * Keith Packard 28235783Skib * Eric Anholt <eric@anholt.net> 29235783Skib * Dave Airlie <airlied@linux.ie> 30235783Skib * Jesse Barnes <jesse.barnes@intel.com> 31235783Skib */ 32235783Skib#include <sys/cdefs.h> 33235783Skib__FBSDID("$FreeBSD$"); 34235783Skib 35235783Skib#include <dev/drm2/drm.h> 36235783Skib#include <dev/drm2/drmP.h> 37235783Skib#include <dev/drm2/drm_crtc.h> 38235783Skib#include <dev/drm2/drm_edid.h> 39235783Skib#include <dev/drm2/drm_fourcc.h> 40235783Skib#include <sys/limits.h> 41235783Skib 42235783Skib/* Avoid boilerplate. I'm tired of typing. */ 43235783Skib#define DRM_ENUM_NAME_FN(fnname, list) \ 44235783Skib char *fnname(int val) \ 45235783Skib { \ 46235783Skib int i; \ 47235783Skib for (i = 0; i < DRM_ARRAY_SIZE(list); i++) { \ 48235783Skib if (list[i].type == val) \ 49235783Skib return list[i].name; \ 50235783Skib } \ 51235783Skib return "(unknown)"; \ 52235783Skib } 53235783Skib 54235783Skib/* 55235783Skib * Global properties 56235783Skib */ 57235783Skibstatic struct drm_prop_enum_list drm_dpms_enum_list[] = 58235783Skib{ { DRM_MODE_DPMS_ON, "On" }, 59235783Skib { DRM_MODE_DPMS_STANDBY, "Standby" }, 60235783Skib { DRM_MODE_DPMS_SUSPEND, "Suspend" }, 61235783Skib { DRM_MODE_DPMS_OFF, "Off" } 62235783Skib}; 63235783Skib 64235783SkibDRM_ENUM_NAME_FN(drm_get_dpms_name, drm_dpms_enum_list) 65235783Skib 66235783Skib/* 67235783Skib * Optional properties 68235783Skib */ 69235783Skibstatic struct drm_prop_enum_list drm_scaling_mode_enum_list[] = 70235783Skib{ 71235783Skib { DRM_MODE_SCALE_NONE, "None" }, 72235783Skib { DRM_MODE_SCALE_FULLSCREEN, "Full" }, 73235783Skib { DRM_MODE_SCALE_CENTER, "Center" }, 74235783Skib { DRM_MODE_SCALE_ASPECT, "Full aspect" }, 75235783Skib}; 76235783Skib 77235783Skibstatic struct drm_prop_enum_list drm_dithering_mode_enum_list[] = 78235783Skib{ 79235783Skib { DRM_MODE_DITHERING_OFF, "Off" }, 80235783Skib { DRM_MODE_DITHERING_ON, "On" }, 81235783Skib { DRM_MODE_DITHERING_AUTO, "Automatic" }, 82235783Skib}; 83235783Skib 84235783Skib/* 85235783Skib * Non-global properties, but "required" for certain connectors. 86235783Skib */ 87235783Skibstatic struct drm_prop_enum_list drm_dvi_i_select_enum_list[] = 88235783Skib{ 89235783Skib { DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */ 90235783Skib { DRM_MODE_SUBCONNECTOR_DVID, "DVI-D" }, /* DVI-I */ 91235783Skib { DRM_MODE_SUBCONNECTOR_DVIA, "DVI-A" }, /* DVI-I */ 92235783Skib}; 93235783Skib 94235783SkibDRM_ENUM_NAME_FN(drm_get_dvi_i_select_name, drm_dvi_i_select_enum_list) 95235783Skib 96235783Skibstatic struct drm_prop_enum_list drm_dvi_i_subconnector_enum_list[] = 97235783Skib{ 98235783Skib { DRM_MODE_SUBCONNECTOR_Unknown, "Unknown" }, /* DVI-I and TV-out */ 99235783Skib { DRM_MODE_SUBCONNECTOR_DVID, "DVI-D" }, /* DVI-I */ 100235783Skib { DRM_MODE_SUBCONNECTOR_DVIA, "DVI-A" }, /* DVI-I */ 101235783Skib}; 102235783Skib 103235783SkibDRM_ENUM_NAME_FN(drm_get_dvi_i_subconnector_name, 104235783Skib drm_dvi_i_subconnector_enum_list) 105235783Skib 106235783Skibstatic struct drm_prop_enum_list drm_tv_select_enum_list[] = 107235783Skib{ 108235783Skib { DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */ 109235783Skib { DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */ 110235783Skib { DRM_MODE_SUBCONNECTOR_SVIDEO, "SVIDEO" }, /* TV-out */ 111235783Skib { DRM_MODE_SUBCONNECTOR_Component, "Component" }, /* TV-out */ 112235783Skib { DRM_MODE_SUBCONNECTOR_SCART, "SCART" }, /* TV-out */ 113235783Skib}; 114235783Skib 115235783SkibDRM_ENUM_NAME_FN(drm_get_tv_select_name, drm_tv_select_enum_list) 116235783Skib 117235783Skibstatic struct drm_prop_enum_list drm_tv_subconnector_enum_list[] = 118235783Skib{ 119235783Skib { DRM_MODE_SUBCONNECTOR_Unknown, "Unknown" }, /* DVI-I and TV-out */ 120235783Skib { DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */ 121235783Skib { DRM_MODE_SUBCONNECTOR_SVIDEO, "SVIDEO" }, /* TV-out */ 122235783Skib { DRM_MODE_SUBCONNECTOR_Component, "Component" }, /* TV-out */ 123235783Skib { DRM_MODE_SUBCONNECTOR_SCART, "SCART" }, /* TV-out */ 124235783Skib}; 125235783Skib 126235783SkibDRM_ENUM_NAME_FN(drm_get_tv_subconnector_name, 127235783Skib drm_tv_subconnector_enum_list) 128235783Skib 129235783Skibstatic struct drm_prop_enum_list drm_dirty_info_enum_list[] = { 130235783Skib { DRM_MODE_DIRTY_OFF, "Off" }, 131235783Skib { DRM_MODE_DIRTY_ON, "On" }, 132235783Skib { DRM_MODE_DIRTY_ANNOTATE, "Annotate" }, 133235783Skib}; 134235783Skib 135235783SkibDRM_ENUM_NAME_FN(drm_get_dirty_info_name, 136235783Skib drm_dirty_info_enum_list) 137235783Skib 138235783Skibstruct drm_conn_prop_enum_list { 139235783Skib int type; 140235783Skib char *name; 141235783Skib int count; 142235783Skib}; 143235783Skib 144235783Skib/* 145235783Skib * Connector and encoder types. 146235783Skib */ 147235783Skibstatic struct drm_conn_prop_enum_list drm_connector_enum_list[] = 148235783Skib{ { DRM_MODE_CONNECTOR_Unknown, "Unknown", 0 }, 149235783Skib { DRM_MODE_CONNECTOR_VGA, "VGA", 0 }, 150235783Skib { DRM_MODE_CONNECTOR_DVII, "DVI-I", 0 }, 151235783Skib { DRM_MODE_CONNECTOR_DVID, "DVI-D", 0 }, 152235783Skib { DRM_MODE_CONNECTOR_DVIA, "DVI-A", 0 }, 153235783Skib { DRM_MODE_CONNECTOR_Composite, "Composite", 0 }, 154235783Skib { DRM_MODE_CONNECTOR_SVIDEO, "SVIDEO", 0 }, 155235783Skib { DRM_MODE_CONNECTOR_LVDS, "LVDS", 0 }, 156235783Skib { DRM_MODE_CONNECTOR_Component, "Component", 0 }, 157235783Skib { DRM_MODE_CONNECTOR_9PinDIN, "DIN", 0 }, 158235783Skib { DRM_MODE_CONNECTOR_DisplayPort, "DP", 0 }, 159235783Skib { DRM_MODE_CONNECTOR_HDMIA, "HDMI-A", 0 }, 160235783Skib { DRM_MODE_CONNECTOR_HDMIB, "HDMI-B", 0 }, 161235783Skib { DRM_MODE_CONNECTOR_TV, "TV", 0 }, 162235783Skib { DRM_MODE_CONNECTOR_eDP, "eDP", 0 }, 163235783Skib}; 164235783Skib 165235783Skibstatic struct drm_prop_enum_list drm_encoder_enum_list[] = 166235783Skib{ { DRM_MODE_ENCODER_NONE, "None" }, 167235783Skib { DRM_MODE_ENCODER_DAC, "DAC" }, 168235783Skib { DRM_MODE_ENCODER_TMDS, "TMDS" }, 169235783Skib { DRM_MODE_ENCODER_LVDS, "LVDS" }, 170235783Skib { DRM_MODE_ENCODER_TVDAC, "TV" }, 171235783Skib}; 172235783Skib 173261632Sdumbbellstatic void drm_property_destroy_blob(struct drm_device *dev, 174261632Sdumbbell struct drm_property_blob *blob); 175261632Sdumbbell 176235783Skibchar *drm_get_encoder_name(struct drm_encoder *encoder) 177235783Skib{ 178235783Skib static char buf[32]; 179235783Skib 180235783Skib snprintf(buf, 32, "%s-%d", 181235783Skib drm_encoder_enum_list[encoder->encoder_type].name, 182235783Skib encoder->base.id); 183235783Skib return buf; 184235783Skib} 185235783Skib 186235783Skibchar *drm_get_connector_name(struct drm_connector *connector) 187235783Skib{ 188235783Skib static char buf[32]; 189235783Skib 190235783Skib snprintf(buf, 32, "%s-%d", 191235783Skib drm_connector_enum_list[connector->connector_type].name, 192235783Skib connector->connector_type_id); 193235783Skib return buf; 194235783Skib} 195235783Skib 196235783Skibchar *drm_get_connector_status_name(enum drm_connector_status status) 197235783Skib{ 198235783Skib if (status == connector_status_connected) 199235783Skib return "connected"; 200235783Skib else if (status == connector_status_disconnected) 201235783Skib return "disconnected"; 202235783Skib else 203235783Skib return "unknown"; 204235783Skib} 205235783Skib 206235783Skib/** 207235783Skib * drm_mode_object_get - allocate a new identifier 208235783Skib * @dev: DRM device 209235783Skib * @ptr: object pointer, used to generate unique ID 210235783Skib * @type: object type 211235783Skib * 212235783Skib * LOCKING: 213235783Skib * 214235783Skib * Create a unique identifier based on @ptr in @dev's identifier space. Used 215235783Skib * for tracking modes, CRTCs and connectors. 216235783Skib * 217235783Skib * RETURNS: 218235783Skib * New unique (relative to other objects in @dev) integer identifier for the 219235783Skib * object. 220235783Skib */ 221235783Skibstatic int drm_mode_object_get(struct drm_device *dev, 222235783Skib struct drm_mode_object *obj, uint32_t obj_type) 223235783Skib{ 224235783Skib int new_id; 225235783Skib int ret; 226235783Skib 227235783Skib new_id = 0; 228235783Skib ret = drm_gem_name_create(&dev->mode_config.crtc_names, obj, &new_id); 229235783Skib if (ret != 0) 230235783Skib return (ret); 231235783Skib 232235783Skib obj->id = new_id; 233235783Skib obj->type = obj_type; 234235783Skib return 0; 235235783Skib} 236235783Skib 237235783Skib/** 238235783Skib * drm_mode_object_put - free an identifer 239235783Skib * @dev: DRM device 240235783Skib * @id: ID to free 241235783Skib * 242235783Skib * LOCKING: 243235783Skib * Caller must hold DRM mode_config lock. 244235783Skib * 245235783Skib * Free @id from @dev's unique identifier pool. 246235783Skib */ 247235783Skibstatic void drm_mode_object_put(struct drm_device *dev, 248235783Skib struct drm_mode_object *object) 249235783Skib{ 250235783Skib 251235783Skib drm_gem_names_remove(&dev->mode_config.crtc_names, object->id); 252235783Skib} 253235783Skib 254235783Skibstruct drm_mode_object *drm_mode_object_find(struct drm_device *dev, 255235783Skib uint32_t id, uint32_t type) 256235783Skib{ 257235783Skib struct drm_mode_object *obj; 258235783Skib 259235783Skib obj = drm_gem_name_ref(&dev->mode_config.crtc_names, id, NULL); 260235783Skib if (!obj || (obj->type != type) || (obj->id != id)) 261235783Skib obj = NULL; 262235783Skib 263235783Skib return obj; 264235783Skib} 265235783Skib 266235783Skib/** 267235783Skib * drm_framebuffer_init - initialize a framebuffer 268235783Skib * @dev: DRM device 269235783Skib * 270235783Skib * LOCKING: 271235783Skib * Caller must hold mode config lock. 272235783Skib * 273235783Skib * Allocates an ID for the framebuffer's parent mode object, sets its mode 274235783Skib * functions & device file and adds it to the master fd list. 275235783Skib * 276235783Skib * RETURNS: 277235783Skib * Zero on success, error code on failure. 278235783Skib */ 279235783Skibint drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb, 280235783Skib const struct drm_framebuffer_funcs *funcs) 281235783Skib{ 282235783Skib int ret; 283235783Skib 284235783Skib DRM_MODE_CONFIG_ASSERT_LOCKED(dev); 285235783Skib 286235783Skib ret = drm_mode_object_get(dev, &fb->base, DRM_MODE_OBJECT_FB); 287235783Skib if (ret) 288235783Skib return ret; 289235783Skib 290235783Skib fb->dev = dev; 291235783Skib fb->funcs = funcs; 292235783Skib dev->mode_config.num_fb++; 293235783Skib list_add(&fb->head, &dev->mode_config.fb_list); 294235783Skib 295235783Skib return 0; 296235783Skib} 297235783Skib 298235783Skib/** 299235783Skib * drm_framebuffer_cleanup - remove a framebuffer object 300235783Skib * @fb: framebuffer to remove 301235783Skib * 302235783Skib * LOCKING: 303235783Skib * Caller must hold mode config lock. 304235783Skib * 305235783Skib * Scans all the CRTCs in @dev's mode_config. If they're using @fb, removes 306235783Skib * it, setting it to NULL. 307235783Skib */ 308235783Skibvoid drm_framebuffer_cleanup(struct drm_framebuffer *fb) 309235783Skib{ 310235783Skib struct drm_device *dev = fb->dev; 311235783Skib struct drm_crtc *crtc; 312235783Skib struct drm_plane *plane; 313235783Skib struct drm_mode_set set; 314235783Skib int ret; 315235783Skib 316235783Skib DRM_MODE_CONFIG_ASSERT_LOCKED(dev); 317235783Skib 318235783Skib /* remove from any CRTC */ 319235783Skib list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { 320235783Skib if (crtc->fb == fb) { 321235783Skib /* should turn off the crtc */ 322235783Skib memset(&set, 0, sizeof(struct drm_mode_set)); 323235783Skib set.crtc = crtc; 324235783Skib set.fb = NULL; 325235783Skib ret = crtc->funcs->set_config(&set); 326235783Skib if (ret) 327235783Skib DRM_ERROR("failed to reset crtc %p when fb was deleted\n", crtc); 328235783Skib } 329235783Skib } 330235783Skib 331235783Skib list_for_each_entry(plane, &dev->mode_config.plane_list, head) { 332235783Skib if (plane->fb == fb) { 333235783Skib /* should turn off the crtc */ 334235783Skib ret = plane->funcs->disable_plane(plane); 335235783Skib if (ret) 336235783Skib DRM_ERROR("failed to disable plane with busy fb\n"); 337235783Skib /* disconnect the plane from the fb and crtc: */ 338235783Skib plane->fb = NULL; 339235783Skib plane->crtc = NULL; 340235783Skib } 341235783Skib } 342235783Skib 343235783Skib drm_mode_object_put(dev, &fb->base); 344235783Skib list_del(&fb->head); 345235783Skib dev->mode_config.num_fb--; 346235783Skib} 347235783Skib 348235783Skib/** 349235783Skib * drm_crtc_init - Initialise a new CRTC object 350235783Skib * @dev: DRM device 351235783Skib * @crtc: CRTC object to init 352235783Skib * @funcs: callbacks for the new CRTC 353235783Skib * 354235783Skib * LOCKING: 355235783Skib * Caller must hold mode config lock. 356235783Skib * 357235783Skib * Inits a new object created as base part of an driver crtc object. 358235783Skib * 359235783Skib * RETURNS: 360235783Skib * Zero on success, error code on failure. 361235783Skib */ 362235783Skibint drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, 363235783Skib const struct drm_crtc_funcs *funcs) 364235783Skib{ 365235783Skib int ret; 366235783Skib 367235783Skib crtc->dev = dev; 368235783Skib crtc->funcs = funcs; 369235783Skib 370235783Skib sx_xlock(&dev->mode_config.mutex); 371235783Skib ret = drm_mode_object_get(dev, &crtc->base, DRM_MODE_OBJECT_CRTC); 372235783Skib if (ret) 373235783Skib goto out; 374235783Skib 375235783Skib list_add_tail(&crtc->head, &dev->mode_config.crtc_list); 376235783Skib dev->mode_config.num_crtc++; 377235783Skibout: 378235783Skib sx_xunlock(&dev->mode_config.mutex); 379235783Skib 380235783Skib return ret; 381235783Skib} 382235783Skib 383235783Skib/** 384235783Skib * drm_crtc_cleanup - Cleans up the core crtc usage. 385235783Skib * @crtc: CRTC to cleanup 386235783Skib * 387235783Skib * LOCKING: 388235783Skib * Caller must hold mode config lock. 389235783Skib * 390235783Skib * Cleanup @crtc. Removes from drm modesetting space 391235783Skib * does NOT free object, caller does that. 392235783Skib */ 393235783Skibvoid drm_crtc_cleanup(struct drm_crtc *crtc) 394235783Skib{ 395235783Skib struct drm_device *dev = crtc->dev; 396235783Skib 397235783Skib DRM_MODE_CONFIG_ASSERT_LOCKED(dev); 398235783Skib 399235783Skib if (crtc->gamma_store) { 400235783Skib free(crtc->gamma_store, DRM_MEM_KMS); 401235783Skib crtc->gamma_store = NULL; 402235783Skib } 403235783Skib 404235783Skib drm_mode_object_put(dev, &crtc->base); 405235783Skib list_del(&crtc->head); 406235783Skib dev->mode_config.num_crtc--; 407235783Skib} 408235783Skib 409235783Skib/** 410235783Skib * drm_mode_probed_add - add a mode to a connector's probed mode list 411235783Skib * @connector: connector the new mode 412235783Skib * @mode: mode data 413235783Skib * 414235783Skib * LOCKING: 415235783Skib * Caller must hold mode config lock. 416235783Skib * 417235783Skib * Add @mode to @connector's mode list for later use. 418235783Skib */ 419235783Skibvoid drm_mode_probed_add(struct drm_connector *connector, 420235783Skib struct drm_display_mode *mode) 421235783Skib{ 422235783Skib 423235783Skib DRM_MODE_CONFIG_ASSERT_LOCKED(connector->dev); 424235783Skib 425235783Skib list_add(&mode->head, &connector->probed_modes); 426235783Skib} 427235783Skib 428235783Skib/** 429235783Skib * drm_mode_remove - remove and free a mode 430235783Skib * @connector: connector list to modify 431235783Skib * @mode: mode to remove 432235783Skib * 433235783Skib * LOCKING: 434235783Skib * Caller must hold mode config lock. 435235783Skib * 436235783Skib * Remove @mode from @connector's mode list, then free it. 437235783Skib */ 438235783Skibvoid drm_mode_remove(struct drm_connector *connector, 439235783Skib struct drm_display_mode *mode) 440235783Skib{ 441235783Skib 442235783Skib DRM_MODE_CONFIG_ASSERT_LOCKED(connector->dev); 443235783Skib 444235783Skib list_del(&mode->head); 445235783Skib drm_mode_destroy(connector->dev, mode); 446235783Skib} 447235783Skib 448235783Skib/** 449235783Skib * drm_connector_init - Init a preallocated connector 450235783Skib * @dev: DRM device 451235783Skib * @connector: the connector to init 452235783Skib * @funcs: callbacks for this connector 453235783Skib * @name: user visible name of the connector 454235783Skib * 455235783Skib * LOCKING: 456235783Skib * Takes mode config lock. 457235783Skib * 458235783Skib * Initialises a preallocated connector. Connectors should be 459235783Skib * subclassed as part of driver connector objects. 460235783Skib * 461235783Skib * RETURNS: 462235783Skib * Zero on success, error code on failure. 463235783Skib */ 464235783Skibint drm_connector_init(struct drm_device *dev, 465235783Skib struct drm_connector *connector, 466235783Skib const struct drm_connector_funcs *funcs, 467235783Skib int connector_type) 468235783Skib{ 469235783Skib int ret; 470235783Skib 471235783Skib sx_xlock(&dev->mode_config.mutex); 472235783Skib 473235783Skib ret = drm_mode_object_get(dev, &connector->base, DRM_MODE_OBJECT_CONNECTOR); 474235783Skib if (ret) 475235783Skib goto out; 476235783Skib 477235783Skib connector->dev = dev; 478235783Skib connector->funcs = funcs; 479235783Skib connector->connector_type = connector_type; 480235783Skib connector->connector_type_id = 481235783Skib ++drm_connector_enum_list[connector_type].count; /* TODO */ 482235783Skib INIT_LIST_HEAD(&connector->user_modes); 483235783Skib INIT_LIST_HEAD(&connector->probed_modes); 484235783Skib INIT_LIST_HEAD(&connector->modes); 485235783Skib connector->edid_blob_ptr = NULL; 486235783Skib 487235783Skib list_add_tail(&connector->head, &dev->mode_config.connector_list); 488235783Skib dev->mode_config.num_connector++; 489235783Skib 490235783Skib drm_connector_attach_property(connector, 491235783Skib dev->mode_config.edid_property, 0); 492235783Skib 493235783Skib drm_connector_attach_property(connector, 494235783Skib dev->mode_config.dpms_property, 0); 495235783Skib 496235783Skibout: 497235783Skib sx_xunlock(&dev->mode_config.mutex); 498235783Skib 499235783Skib return ret; 500235783Skib} 501235783Skib 502235783Skib/** 503235783Skib * drm_connector_cleanup - cleans up an initialised connector 504235783Skib * @connector: connector to cleanup 505235783Skib * 506235783Skib * LOCKING: 507235783Skib * Takes mode config lock. 508235783Skib * 509235783Skib * Cleans up the connector but doesn't free the object. 510235783Skib */ 511235783Skibvoid drm_connector_cleanup(struct drm_connector *connector) 512235783Skib{ 513235783Skib struct drm_device *dev = connector->dev; 514235783Skib struct drm_display_mode *mode, *t; 515235783Skib 516235783Skib list_for_each_entry_safe(mode, t, &connector->probed_modes, head) 517235783Skib drm_mode_remove(connector, mode); 518235783Skib 519235783Skib list_for_each_entry_safe(mode, t, &connector->modes, head) 520235783Skib drm_mode_remove(connector, mode); 521235783Skib 522235783Skib list_for_each_entry_safe(mode, t, &connector->user_modes, head) 523235783Skib drm_mode_remove(connector, mode); 524235783Skib 525235783Skib sx_xlock(&dev->mode_config.mutex); 526261632Sdumbbell if (connector->edid_blob_ptr) 527261632Sdumbbell drm_property_destroy_blob(dev, connector->edid_blob_ptr); 528235783Skib drm_mode_object_put(dev, &connector->base); 529235783Skib list_del(&connector->head); 530235783Skib dev->mode_config.num_connector--; 531235783Skib sx_xunlock(&dev->mode_config.mutex); 532235783Skib} 533235783Skib 534235783Skibint drm_encoder_init(struct drm_device *dev, 535235783Skib struct drm_encoder *encoder, 536235783Skib const struct drm_encoder_funcs *funcs, 537235783Skib int encoder_type) 538235783Skib{ 539235783Skib int ret; 540235783Skib 541235783Skib sx_xlock(&dev->mode_config.mutex); 542235783Skib 543235783Skib ret = drm_mode_object_get(dev, &encoder->base, DRM_MODE_OBJECT_ENCODER); 544235783Skib if (ret) 545235783Skib goto out; 546235783Skib 547235783Skib encoder->dev = dev; 548235783Skib encoder->encoder_type = encoder_type; 549235783Skib encoder->funcs = funcs; 550235783Skib 551235783Skib list_add_tail(&encoder->head, &dev->mode_config.encoder_list); 552235783Skib dev->mode_config.num_encoder++; 553235783Skib 554235783Skibout: 555235783Skib sx_xunlock(&dev->mode_config.mutex); 556235783Skib 557235783Skib return ret; 558235783Skib} 559235783Skib 560235783Skibvoid drm_encoder_cleanup(struct drm_encoder *encoder) 561235783Skib{ 562235783Skib struct drm_device *dev = encoder->dev; 563235783Skib 564235783Skib sx_xlock(&dev->mode_config.mutex); 565235783Skib drm_mode_object_put(dev, &encoder->base); 566235783Skib list_del(&encoder->head); 567235783Skib dev->mode_config.num_encoder--; 568235783Skib sx_xunlock(&dev->mode_config.mutex); 569235783Skib} 570235783Skib 571235783Skibint drm_plane_init(struct drm_device *dev, struct drm_plane *plane, 572235783Skib unsigned long possible_crtcs, 573235783Skib const struct drm_plane_funcs *funcs, 574235783Skib const uint32_t *formats, uint32_t format_count, 575235783Skib bool priv) 576235783Skib{ 577235783Skib int ret; 578235783Skib 579235783Skib sx_xlock(&dev->mode_config.mutex); 580235783Skib 581235783Skib ret = drm_mode_object_get(dev, &plane->base, DRM_MODE_OBJECT_PLANE); 582235783Skib if (ret) 583235783Skib goto out; 584235783Skib 585235783Skib plane->dev = dev; 586235783Skib plane->funcs = funcs; 587235783Skib plane->format_types = malloc(sizeof(uint32_t) * format_count, 588235783Skib DRM_MEM_KMS, M_WAITOK); 589235783Skib 590235783Skib memcpy(plane->format_types, formats, format_count * sizeof(uint32_t)); 591235783Skib plane->format_count = format_count; 592235783Skib plane->possible_crtcs = possible_crtcs; 593235783Skib 594235783Skib /* private planes are not exposed to userspace, but depending on 595235783Skib * display hardware, might be convenient to allow sharing programming 596235783Skib * for the scanout engine with the crtc implementation. 597235783Skib */ 598235783Skib if (!priv) { 599235783Skib list_add_tail(&plane->head, &dev->mode_config.plane_list); 600235783Skib dev->mode_config.num_plane++; 601235783Skib } else { 602235783Skib INIT_LIST_HEAD(&plane->head); 603235783Skib } 604235783Skib 605235783Skibout: 606235783Skib sx_xunlock(&dev->mode_config.mutex); 607235783Skib 608235783Skib return ret; 609235783Skib} 610235783Skib 611235783Skibvoid drm_plane_cleanup(struct drm_plane *plane) 612235783Skib{ 613235783Skib struct drm_device *dev = plane->dev; 614235783Skib 615235783Skib sx_xlock(&dev->mode_config.mutex); 616235783Skib free(plane->format_types, DRM_MEM_KMS); 617235783Skib drm_mode_object_put(dev, &plane->base); 618235783Skib /* if not added to a list, it must be a private plane */ 619235783Skib if (!list_empty(&plane->head)) { 620235783Skib list_del(&plane->head); 621235783Skib dev->mode_config.num_plane--; 622235783Skib } 623235783Skib sx_xunlock(&dev->mode_config.mutex); 624235783Skib} 625235783Skib 626235783Skib/** 627235783Skib * drm_mode_create - create a new display mode 628235783Skib * @dev: DRM device 629235783Skib * 630235783Skib * LOCKING: 631235783Skib * Caller must hold DRM mode_config lock. 632235783Skib * 633235783Skib * Create a new drm_display_mode, give it an ID, and return it. 634235783Skib * 635235783Skib * RETURNS: 636235783Skib * Pointer to new mode on success, NULL on error. 637235783Skib */ 638235783Skibstruct drm_display_mode *drm_mode_create(struct drm_device *dev) 639235783Skib{ 640235783Skib struct drm_display_mode *nmode; 641235783Skib 642235783Skib nmode = malloc(sizeof(struct drm_display_mode), DRM_MEM_KMS, 643235783Skib M_WAITOK | M_ZERO); 644235783Skib 645235783Skib if (drm_mode_object_get(dev, &nmode->base, DRM_MODE_OBJECT_MODE)) { 646235783Skib free(nmode, DRM_MEM_KMS); 647235783Skib return (NULL); 648235783Skib } 649235783Skib return nmode; 650235783Skib} 651235783Skib 652235783Skib/** 653235783Skib * drm_mode_destroy - remove a mode 654235783Skib * @dev: DRM device 655235783Skib * @mode: mode to remove 656235783Skib * 657235783Skib * LOCKING: 658235783Skib * Caller must hold mode config lock. 659235783Skib * 660235783Skib * Free @mode's unique identifier, then free it. 661235783Skib */ 662235783Skibvoid drm_mode_destroy(struct drm_device *dev, struct drm_display_mode *mode) 663235783Skib{ 664235783Skib if (!mode) 665235783Skib return; 666235783Skib 667235783Skib drm_mode_object_put(dev, &mode->base); 668235783Skib 669235783Skib free(mode, DRM_MEM_KMS); 670235783Skib} 671235783Skib 672235783Skibstatic int drm_mode_create_standard_connector_properties(struct drm_device *dev) 673235783Skib{ 674235783Skib struct drm_property *edid; 675235783Skib struct drm_property *dpms; 676235783Skib 677235783Skib /* 678235783Skib * Standard properties (apply to all connectors) 679235783Skib */ 680235783Skib edid = drm_property_create(dev, DRM_MODE_PROP_BLOB | 681235783Skib DRM_MODE_PROP_IMMUTABLE, 682235783Skib "EDID", 0); 683235783Skib dev->mode_config.edid_property = edid; 684235783Skib 685235783Skib dpms = drm_property_create_enum(dev, 0, 686235783Skib "DPMS", drm_dpms_enum_list, 687235783Skib DRM_ARRAY_SIZE(drm_dpms_enum_list)); 688235783Skib dev->mode_config.dpms_property = dpms; 689235783Skib 690235783Skib return 0; 691235783Skib} 692235783Skib 693235783Skib/** 694235783Skib * drm_mode_create_dvi_i_properties - create DVI-I specific connector properties 695235783Skib * @dev: DRM device 696235783Skib * 697235783Skib * Called by a driver the first time a DVI-I connector is made. 698235783Skib */ 699235783Skibint drm_mode_create_dvi_i_properties(struct drm_device *dev) 700235783Skib{ 701235783Skib struct drm_property *dvi_i_selector; 702235783Skib struct drm_property *dvi_i_subconnector; 703235783Skib 704235783Skib if (dev->mode_config.dvi_i_select_subconnector_property) 705235783Skib return 0; 706235783Skib 707235783Skib dvi_i_selector = 708235783Skib drm_property_create_enum(dev, 0, 709235783Skib "select subconnector", 710235783Skib drm_dvi_i_select_enum_list, 711235783Skib DRM_ARRAY_SIZE(drm_dvi_i_select_enum_list)); 712235783Skib dev->mode_config.dvi_i_select_subconnector_property = dvi_i_selector; 713235783Skib 714235783Skib dvi_i_subconnector = drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE, 715235783Skib "subconnector", 716235783Skib drm_dvi_i_subconnector_enum_list, 717235783Skib DRM_ARRAY_SIZE(drm_dvi_i_subconnector_enum_list)); 718235783Skib dev->mode_config.dvi_i_subconnector_property = dvi_i_subconnector; 719235783Skib 720235783Skib return 0; 721235783Skib} 722235783Skib 723235783Skib/** 724235783Skib * drm_create_tv_properties - create TV specific connector properties 725235783Skib * @dev: DRM device 726235783Skib * @num_modes: number of different TV formats (modes) supported 727235783Skib * @modes: array of pointers to strings containing name of each format 728235783Skib * 729235783Skib * Called by a driver's TV initialization routine, this function creates 730235783Skib * the TV specific connector properties for a given device. Caller is 731235783Skib * responsible for allocating a list of format names and passing them to 732235783Skib * this routine. 733235783Skib */ 734235783Skibint drm_mode_create_tv_properties(struct drm_device *dev, int num_modes, 735235783Skib char *modes[]) 736235783Skib{ 737235783Skib struct drm_property *tv_selector; 738235783Skib struct drm_property *tv_subconnector; 739235783Skib int i; 740235783Skib 741235783Skib if (dev->mode_config.tv_select_subconnector_property) 742235783Skib return 0; 743235783Skib 744235783Skib /* 745235783Skib * Basic connector properties 746235783Skib */ 747235783Skib tv_selector = drm_property_create_enum(dev, 0, 748235783Skib "select subconnector", 749235783Skib drm_tv_select_enum_list, 750235783Skib DRM_ARRAY_SIZE(drm_tv_select_enum_list)); 751235783Skib dev->mode_config.tv_select_subconnector_property = tv_selector; 752235783Skib 753235783Skib tv_subconnector = 754235783Skib drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE, 755235783Skib "subconnector", 756235783Skib drm_tv_subconnector_enum_list, 757235783Skib DRM_ARRAY_SIZE(drm_tv_subconnector_enum_list)); 758235783Skib dev->mode_config.tv_subconnector_property = tv_subconnector; 759235783Skib 760235783Skib /* 761235783Skib * Other, TV specific properties: margins & TV modes. 762235783Skib */ 763235783Skib dev->mode_config.tv_left_margin_property = 764235783Skib drm_property_create_range(dev, 0, "left margin", 0, 100); 765235783Skib 766235783Skib dev->mode_config.tv_right_margin_property = 767235783Skib drm_property_create_range(dev, 0, "right margin", 0, 100); 768235783Skib 769235783Skib dev->mode_config.tv_top_margin_property = 770235783Skib drm_property_create_range(dev, 0, "top margin", 0, 100); 771235783Skib 772235783Skib dev->mode_config.tv_bottom_margin_property = 773235783Skib drm_property_create_range(dev, 0, "bottom margin", 0, 100); 774235783Skib 775235783Skib dev->mode_config.tv_mode_property = 776235783Skib drm_property_create(dev, DRM_MODE_PROP_ENUM, 777235783Skib "mode", num_modes); 778235783Skib for (i = 0; i < num_modes; i++) 779235783Skib drm_property_add_enum(dev->mode_config.tv_mode_property, i, 780235783Skib i, modes[i]); 781235783Skib 782235783Skib dev->mode_config.tv_brightness_property = 783235783Skib drm_property_create_range(dev, 0, "brightness", 0, 100); 784235783Skib 785235783Skib dev->mode_config.tv_contrast_property = 786235783Skib drm_property_create_range(dev, 0, "contrast", 0, 100); 787235783Skib 788235783Skib dev->mode_config.tv_flicker_reduction_property = 789235783Skib drm_property_create_range(dev, 0, "flicker reduction", 0, 100); 790235783Skib 791235783Skib dev->mode_config.tv_overscan_property = 792235783Skib drm_property_create_range(dev, 0, "overscan", 0, 100); 793235783Skib 794235783Skib dev->mode_config.tv_saturation_property = 795235783Skib drm_property_create_range(dev, 0, "saturation", 0, 100); 796235783Skib 797235783Skib dev->mode_config.tv_hue_property = 798235783Skib drm_property_create_range(dev, 0, "hue", 0, 100); 799235783Skib 800235783Skib return 0; 801235783Skib} 802235783Skib 803235783Skib/** 804235783Skib * drm_mode_create_scaling_mode_property - create scaling mode property 805235783Skib * @dev: DRM device 806235783Skib * 807235783Skib * Called by a driver the first time it's needed, must be attached to desired 808235783Skib * connectors. 809235783Skib */ 810235783Skibint drm_mode_create_scaling_mode_property(struct drm_device *dev) 811235783Skib{ 812235783Skib struct drm_property *scaling_mode; 813235783Skib 814235783Skib if (dev->mode_config.scaling_mode_property) 815235783Skib return 0; 816235783Skib 817235783Skib scaling_mode = 818235783Skib drm_property_create_enum(dev, 0, "scaling mode", 819235783Skib drm_scaling_mode_enum_list, 820235783Skib DRM_ARRAY_SIZE(drm_scaling_mode_enum_list)); 821235783Skib 822235783Skib dev->mode_config.scaling_mode_property = scaling_mode; 823235783Skib 824235783Skib return 0; 825235783Skib} 826235783Skib 827235783Skib/** 828235783Skib * drm_mode_create_dithering_property - create dithering property 829235783Skib * @dev: DRM device 830235783Skib * 831235783Skib * Called by a driver the first time it's needed, must be attached to desired 832235783Skib * connectors. 833235783Skib */ 834235783Skibint drm_mode_create_dithering_property(struct drm_device *dev) 835235783Skib{ 836235783Skib struct drm_property *dithering_mode; 837235783Skib 838235783Skib if (dev->mode_config.dithering_mode_property) 839235783Skib return 0; 840235783Skib 841235783Skib dithering_mode = 842235783Skib drm_property_create_enum(dev, 0, "dithering", 843235783Skib drm_dithering_mode_enum_list, 844235783Skib DRM_ARRAY_SIZE(drm_dithering_mode_enum_list)); 845235783Skib dev->mode_config.dithering_mode_property = dithering_mode; 846235783Skib 847235783Skib return 0; 848235783Skib} 849235783Skib 850235783Skib/** 851235783Skib * drm_mode_create_dirty_property - create dirty property 852235783Skib * @dev: DRM device 853235783Skib * 854235783Skib * Called by a driver the first time it's needed, must be attached to desired 855235783Skib * connectors. 856235783Skib */ 857235783Skibint drm_mode_create_dirty_info_property(struct drm_device *dev) 858235783Skib{ 859235783Skib struct drm_property *dirty_info; 860235783Skib 861235783Skib if (dev->mode_config.dirty_info_property) 862235783Skib return 0; 863235783Skib 864235783Skib dirty_info = 865235783Skib drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE, 866235783Skib "dirty", 867235783Skib drm_dirty_info_enum_list, 868235783Skib DRM_ARRAY_SIZE(drm_dirty_info_enum_list)); 869235783Skib dev->mode_config.dirty_info_property = dirty_info; 870235783Skib 871235783Skib return 0; 872235783Skib} 873235783Skib 874235783Skib/** 875235783Skib * drm_mode_config_init - initialize DRM mode_configuration structure 876235783Skib * @dev: DRM device 877235783Skib * 878235783Skib * LOCKING: 879235783Skib * None, should happen single threaded at init time. 880235783Skib * 881235783Skib * Initialize @dev's mode_config structure, used for tracking the graphics 882235783Skib * configuration of @dev. 883235783Skib */ 884235783Skibvoid drm_mode_config_init(struct drm_device *dev) 885235783Skib{ 886235783Skib sx_init(&dev->mode_config.mutex, "kmslk"); 887235783Skib INIT_LIST_HEAD(&dev->mode_config.fb_list); 888235783Skib INIT_LIST_HEAD(&dev->mode_config.crtc_list); 889235783Skib INIT_LIST_HEAD(&dev->mode_config.connector_list); 890235783Skib INIT_LIST_HEAD(&dev->mode_config.encoder_list); 891235783Skib INIT_LIST_HEAD(&dev->mode_config.property_list); 892235783Skib INIT_LIST_HEAD(&dev->mode_config.property_blob_list); 893235783Skib INIT_LIST_HEAD(&dev->mode_config.plane_list); 894235783Skib drm_gem_names_init(&dev->mode_config.crtc_names); 895235783Skib 896235783Skib sx_xlock(&dev->mode_config.mutex); 897235783Skib drm_mode_create_standard_connector_properties(dev); 898235783Skib sx_xunlock(&dev->mode_config.mutex); 899235783Skib 900235783Skib /* Just to be sure */ 901235783Skib dev->mode_config.num_fb = 0; 902235783Skib dev->mode_config.num_connector = 0; 903235783Skib dev->mode_config.num_crtc = 0; 904235783Skib dev->mode_config.num_encoder = 0; 905235783Skib} 906235783Skib 907235783Skibstatic int 908235783Skibdrm_mode_group_init(struct drm_device *dev, struct drm_mode_group *group) 909235783Skib{ 910235783Skib uint32_t total_objects = 0; 911235783Skib 912235783Skib total_objects += dev->mode_config.num_crtc; 913235783Skib total_objects += dev->mode_config.num_connector; 914235783Skib total_objects += dev->mode_config.num_encoder; 915235783Skib 916235783Skib group->id_list = malloc(total_objects * sizeof(uint32_t), 917235783Skib DRM_MEM_KMS, M_WAITOK | M_ZERO); 918235783Skib 919235783Skib group->num_crtcs = 0; 920235783Skib group->num_connectors = 0; 921235783Skib group->num_encoders = 0; 922235783Skib return 0; 923235783Skib} 924235783Skib 925235783Skibint drm_mode_group_init_legacy_group(struct drm_device *dev, 926235783Skib struct drm_mode_group *group) 927235783Skib{ 928235783Skib struct drm_crtc *crtc; 929235783Skib struct drm_encoder *encoder; 930235783Skib struct drm_connector *connector; 931235783Skib int ret; 932235783Skib 933235783Skib if ((ret = drm_mode_group_init(dev, group))) 934235783Skib return ret; 935235783Skib 936235783Skib list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) 937235783Skib group->id_list[group->num_crtcs++] = crtc->base.id; 938235783Skib 939235783Skib list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) 940235783Skib group->id_list[group->num_crtcs + group->num_encoders++] = 941235783Skib encoder->base.id; 942235783Skib 943235783Skib list_for_each_entry(connector, &dev->mode_config.connector_list, head) 944235783Skib group->id_list[group->num_crtcs + group->num_encoders + 945235783Skib group->num_connectors++] = connector->base.id; 946235783Skib 947235783Skib return 0; 948235783Skib} 949235783Skib 950235783Skib/** 951235783Skib * drm_mode_config_cleanup - free up DRM mode_config info 952235783Skib * @dev: DRM device 953235783Skib * 954235783Skib * LOCKING: 955235783Skib * Caller must hold mode config lock. 956235783Skib * 957235783Skib * Free up all the connectors and CRTCs associated with this DRM device, then 958235783Skib * free up the framebuffers and associated buffer objects. 959235783Skib * 960235783Skib * FIXME: cleanup any dangling user buffer objects too 961235783Skib */ 962235783Skibvoid drm_mode_config_cleanup(struct drm_device *dev) 963235783Skib{ 964235783Skib struct drm_connector *connector, *ot; 965235783Skib struct drm_crtc *crtc, *ct; 966235783Skib struct drm_encoder *encoder, *enct; 967235783Skib struct drm_framebuffer *fb, *fbt; 968235783Skib struct drm_property *property, *pt; 969235783Skib struct drm_plane *plane, *plt; 970235783Skib 971235783Skib list_for_each_entry_safe(encoder, enct, &dev->mode_config.encoder_list, 972235783Skib head) { 973235783Skib encoder->funcs->destroy(encoder); 974235783Skib } 975235783Skib 976235783Skib list_for_each_entry_safe(connector, ot, 977235783Skib &dev->mode_config.connector_list, head) { 978235783Skib connector->funcs->destroy(connector); 979235783Skib } 980235783Skib 981235783Skib list_for_each_entry_safe(property, pt, &dev->mode_config.property_list, 982235783Skib head) { 983235783Skib drm_property_destroy(dev, property); 984235783Skib } 985235783Skib 986235783Skib list_for_each_entry_safe(fb, fbt, &dev->mode_config.fb_list, head) { 987235783Skib fb->funcs->destroy(fb); 988235783Skib } 989235783Skib 990235783Skib list_for_each_entry_safe(crtc, ct, &dev->mode_config.crtc_list, head) { 991235783Skib crtc->funcs->destroy(crtc); 992235783Skib } 993235783Skib 994235783Skib list_for_each_entry_safe(plane, plt, &dev->mode_config.plane_list, 995235783Skib head) { 996235783Skib plane->funcs->destroy(plane); 997235783Skib } 998235783Skib drm_gem_names_fini(&dev->mode_config.crtc_names); 999235783Skib} 1000235783Skib 1001235783Skib/** 1002235783Skib * drm_crtc_convert_to_umode - convert a drm_display_mode into a modeinfo 1003235783Skib * @out: drm_mode_modeinfo struct to return to the user 1004235783Skib * @in: drm_display_mode to use 1005235783Skib * 1006235783Skib * LOCKING: 1007235783Skib * None. 1008235783Skib * 1009235783Skib * Convert a drm_display_mode into a drm_mode_modeinfo structure to return to 1010235783Skib * the user. 1011235783Skib */ 1012235783Skibstatic void drm_crtc_convert_to_umode(struct drm_mode_modeinfo *out, 1013235783Skib const struct drm_display_mode *in) 1014235783Skib{ 1015235783Skib if (in->hdisplay > USHRT_MAX || in->hsync_start > USHRT_MAX || 1016235783Skib in->hsync_end > USHRT_MAX || in->htotal > USHRT_MAX || 1017235783Skib in->hskew > USHRT_MAX || in->vdisplay > USHRT_MAX || 1018235783Skib in->vsync_start > USHRT_MAX || in->vsync_end > USHRT_MAX || 1019235783Skib in->vtotal > USHRT_MAX || in->vscan > USHRT_MAX) 1020235783Skib printf("timing values too large for mode info\n"); 1021235783Skib 1022235783Skib out->clock = in->clock; 1023235783Skib out->hdisplay = in->hdisplay; 1024235783Skib out->hsync_start = in->hsync_start; 1025235783Skib out->hsync_end = in->hsync_end; 1026235783Skib out->htotal = in->htotal; 1027235783Skib out->hskew = in->hskew; 1028235783Skib out->vdisplay = in->vdisplay; 1029235783Skib out->vsync_start = in->vsync_start; 1030235783Skib out->vsync_end = in->vsync_end; 1031235783Skib out->vtotal = in->vtotal; 1032235783Skib out->vscan = in->vscan; 1033235783Skib out->vrefresh = in->vrefresh; 1034235783Skib out->flags = in->flags; 1035235783Skib out->type = in->type; 1036235783Skib strncpy(out->name, in->name, DRM_DISPLAY_MODE_LEN); 1037235783Skib out->name[DRM_DISPLAY_MODE_LEN-1] = 0; 1038235783Skib} 1039235783Skib 1040235783Skib/** 1041235783Skib * drm_crtc_convert_to_umode - convert a modeinfo into a drm_display_mode 1042235783Skib * @out: drm_display_mode to return to the user 1043235783Skib * @in: drm_mode_modeinfo to use 1044235783Skib * 1045235783Skib * LOCKING: 1046235783Skib * None. 1047235783Skib * 1048235783Skib * Convert a drm_mode_modeinfo into a drm_display_mode structure to return to 1049235783Skib * the caller. 1050235783Skib * 1051235783Skib * RETURNS: 1052235783Skib * Zero on success, errno on failure. 1053235783Skib */ 1054235783Skibstatic int drm_crtc_convert_umode(struct drm_display_mode *out, 1055235783Skib const struct drm_mode_modeinfo *in) 1056235783Skib{ 1057235783Skib if (in->clock > INT_MAX || in->vrefresh > INT_MAX) 1058235783Skib return ERANGE; 1059235783Skib 1060235783Skib out->clock = in->clock; 1061235783Skib out->hdisplay = in->hdisplay; 1062235783Skib out->hsync_start = in->hsync_start; 1063235783Skib out->hsync_end = in->hsync_end; 1064235783Skib out->htotal = in->htotal; 1065235783Skib out->hskew = in->hskew; 1066235783Skib out->vdisplay = in->vdisplay; 1067235783Skib out->vsync_start = in->vsync_start; 1068235783Skib out->vsync_end = in->vsync_end; 1069235783Skib out->vtotal = in->vtotal; 1070235783Skib out->vscan = in->vscan; 1071235783Skib out->vrefresh = in->vrefresh; 1072235783Skib out->flags = in->flags; 1073235783Skib out->type = in->type; 1074235783Skib strncpy(out->name, in->name, DRM_DISPLAY_MODE_LEN); 1075235783Skib out->name[DRM_DISPLAY_MODE_LEN-1] = 0; 1076235783Skib 1077235783Skib return 0; 1078235783Skib} 1079235783Skib 1080235783Skib/** 1081235783Skib * drm_mode_getresources - get graphics configuration 1082235783Skib * @inode: inode from the ioctl 1083235783Skib * @filp: file * from the ioctl 1084235783Skib * @cmd: cmd from ioctl 1085235783Skib * @arg: arg from ioctl 1086235783Skib * 1087235783Skib * LOCKING: 1088235783Skib * Takes mode config lock. 1089235783Skib * 1090235783Skib * Construct a set of configuration description structures and return 1091235783Skib * them to the user, including CRTC, connector and framebuffer configuration. 1092235783Skib * 1093235783Skib * Called by the user via ioctl. 1094235783Skib * 1095235783Skib * RETURNS: 1096235783Skib * Zero on success, errno on failure. 1097235783Skib */ 1098235783Skibint drm_mode_getresources(struct drm_device *dev, void *data, 1099235783Skib struct drm_file *file_priv) 1100235783Skib{ 1101235783Skib struct drm_mode_card_res *card_res = data; 1102235783Skib struct list_head *lh; 1103235783Skib struct drm_framebuffer *fb; 1104235783Skib struct drm_connector *connector; 1105235783Skib struct drm_crtc *crtc; 1106235783Skib struct drm_encoder *encoder; 1107235783Skib int ret = 0; 1108235783Skib int connector_count = 0; 1109235783Skib int crtc_count = 0; 1110235783Skib int fb_count = 0; 1111235783Skib int encoder_count = 0; 1112235783Skib int copied = 0, i; 1113235783Skib uint32_t __user *fb_id; 1114235783Skib uint32_t __user *crtc_id; 1115235783Skib uint32_t __user *connector_id; 1116235783Skib uint32_t __user *encoder_id; 1117235783Skib struct drm_mode_group *mode_group; 1118235783Skib 1119235783Skib if (!drm_core_check_feature(dev, DRIVER_MODESET)) 1120235783Skib return (EINVAL); 1121235783Skib 1122235783Skib sx_xlock(&dev->mode_config.mutex); 1123235783Skib 1124235783Skib /* 1125235783Skib * For the non-control nodes we need to limit the list of resources 1126235783Skib * by IDs in the group list for this node 1127235783Skib */ 1128235783Skib list_for_each(lh, &file_priv->fbs) 1129235783Skib fb_count++; 1130235783Skib 1131235783Skib#if 1 1132235783Skib mode_group = NULL; /* XXXKIB */ 1133235783Skib if (1 || file_priv->master) { 1134235783Skib#else 1135235783Skib mode_group = &file_priv->masterp->minor->mode_group; 1136235783Skib if (file_priv->masterp->minor->type == DRM_MINOR_CONTROL) { 1137235783Skib#endif 1138235783Skib 1139235783Skib list_for_each(lh, &dev->mode_config.crtc_list) 1140235783Skib crtc_count++; 1141235783Skib 1142235783Skib list_for_each(lh, &dev->mode_config.connector_list) 1143235783Skib connector_count++; 1144235783Skib 1145235783Skib list_for_each(lh, &dev->mode_config.encoder_list) 1146235783Skib encoder_count++; 1147235783Skib } else { 1148235783Skib 1149235783Skib crtc_count = mode_group->num_crtcs; 1150235783Skib connector_count = mode_group->num_connectors; 1151235783Skib encoder_count = mode_group->num_encoders; 1152235783Skib } 1153235783Skib 1154235783Skib card_res->max_height = dev->mode_config.max_height; 1155235783Skib card_res->min_height = dev->mode_config.min_height; 1156235783Skib card_res->max_width = dev->mode_config.max_width; 1157235783Skib card_res->min_width = dev->mode_config.min_width; 1158235783Skib 1159235783Skib /* handle this in 4 parts */ 1160235783Skib /* FBs */ 1161235783Skib if (card_res->count_fbs >= fb_count) { 1162235783Skib copied = 0; 1163235783Skib fb_id = (uint32_t *)(uintptr_t)card_res->fb_id_ptr; 1164235783Skib list_for_each_entry(fb, &file_priv->fbs, filp_head) { 1165235783Skib if (copyout(&fb->base.id, fb_id + copied, 1166235783Skib sizeof(uint32_t))) { 1167235783Skib ret = EFAULT; 1168235783Skib goto out; 1169235783Skib } 1170235783Skib copied++; 1171235783Skib } 1172235783Skib } 1173235783Skib card_res->count_fbs = fb_count; 1174235783Skib 1175235783Skib /* CRTCs */ 1176235783Skib if (card_res->count_crtcs >= crtc_count) { 1177235783Skib copied = 0; 1178235783Skib crtc_id = (uint32_t *)(uintptr_t)card_res->crtc_id_ptr; 1179235783Skib#if 1 1180235783Skib if (1 || file_priv->master) { 1181235783Skib#else 1182235783Skib if (file_priv->masterp->minor->type == DRM_MINOR_CONTROL) { 1183235783Skib#endif 1184235783Skib list_for_each_entry(crtc, &dev->mode_config.crtc_list, 1185235783Skib head) { 1186235783Skib DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id); 1187235783Skib if (copyout(&crtc->base.id, crtc_id + 1188235783Skib copied, sizeof(uint32_t))) { 1189235783Skib ret = EFAULT; 1190235783Skib goto out; 1191235783Skib } 1192235783Skib copied++; 1193235783Skib } 1194235783Skib } else { 1195235783Skib for (i = 0; i < mode_group->num_crtcs; i++) { 1196235783Skib if (copyout(&mode_group->id_list[i], 1197235783Skib crtc_id + copied, sizeof(uint32_t))) { 1198235783Skib ret = EFAULT; 1199235783Skib goto out; 1200235783Skib } 1201235783Skib copied++; 1202235783Skib } 1203235783Skib } 1204235783Skib } 1205235783Skib card_res->count_crtcs = crtc_count; 1206235783Skib 1207235783Skib /* Encoders */ 1208235783Skib if (card_res->count_encoders >= encoder_count) { 1209235783Skib copied = 0; 1210235783Skib encoder_id = (uint32_t *)(uintptr_t)card_res->encoder_id_ptr; 1211235783Skib#if 1 1212235783Skib if (file_priv->master) { 1213235783Skib#else 1214235783Skib if (file_priv->masterp->minor->type == DRM_MINOR_CONTROL) { 1215235783Skib#endif 1216235783Skib list_for_each_entry(encoder, 1217235783Skib &dev->mode_config.encoder_list, 1218235783Skib head) { 1219235783Skib DRM_DEBUG_KMS("[ENCODER:%d:%s]\n", encoder->base.id, 1220235783Skib drm_get_encoder_name(encoder)); 1221235783Skib if (copyout(&encoder->base.id, encoder_id + 1222235783Skib copied, sizeof(uint32_t))) { 1223235783Skib ret = EFAULT; 1224235783Skib goto out; 1225235783Skib } 1226235783Skib copied++; 1227235783Skib } 1228235783Skib } else { 1229235783Skib for (i = mode_group->num_crtcs; 1230235783Skib i < mode_group->num_crtcs + mode_group->num_encoders; 1231235783Skib i++) { 1232235783Skib if (copyout(&mode_group->id_list[i], 1233235783Skib encoder_id + copied, sizeof(uint32_t))) { 1234235783Skib ret = EFAULT; 1235235783Skib goto out; 1236235783Skib } 1237235783Skib copied++; 1238235783Skib } 1239235783Skib 1240235783Skib } 1241235783Skib } 1242235783Skib card_res->count_encoders = encoder_count; 1243235783Skib 1244235783Skib /* Connectors */ 1245235783Skib if (card_res->count_connectors >= connector_count) { 1246235783Skib copied = 0; 1247235783Skib connector_id = (uint32_t *)(uintptr_t)card_res->connector_id_ptr; 1248235783Skib#if 1 1249235783Skib if (file_priv->master) { 1250235783Skib#else 1251235783Skib if (file_priv->masterp->minor->type == DRM_MINOR_CONTROL) { 1252235783Skib#endif 1253235783Skib list_for_each_entry(connector, 1254235783Skib &dev->mode_config.connector_list, 1255235783Skib head) { 1256235783Skib DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", 1257235783Skib connector->base.id, 1258235783Skib drm_get_connector_name(connector)); 1259235783Skib if (copyout(&connector->base.id, 1260235783Skib connector_id + copied, sizeof(uint32_t))) { 1261235783Skib ret = EFAULT; 1262235783Skib goto out; 1263235783Skib } 1264235783Skib copied++; 1265235783Skib } 1266235783Skib } else { 1267235783Skib int start = mode_group->num_crtcs + 1268235783Skib mode_group->num_encoders; 1269235783Skib for (i = start; i < start + mode_group->num_connectors; i++) { 1270235783Skib if (copyout(&mode_group->id_list[i], 1271235783Skib connector_id + copied, sizeof(uint32_t))) { 1272235783Skib ret = EFAULT; 1273235783Skib goto out; 1274235783Skib } 1275235783Skib copied++; 1276235783Skib } 1277235783Skib } 1278235783Skib } 1279235783Skib card_res->count_connectors = connector_count; 1280235783Skib 1281235783Skib DRM_DEBUG_KMS("CRTC[%d] CONNECTORS[%d] ENCODERS[%d]\n", card_res->count_crtcs, 1282235783Skib card_res->count_connectors, card_res->count_encoders); 1283235783Skib 1284235783Skibout: 1285235783Skib sx_xunlock(&dev->mode_config.mutex); 1286235783Skib return ret; 1287235783Skib} 1288235783Skib 1289235783Skib/** 1290235783Skib * drm_mode_getcrtc - get CRTC configuration 1291235783Skib * @inode: inode from the ioctl 1292235783Skib * @filp: file * from the ioctl 1293235783Skib * @cmd: cmd from ioctl 1294235783Skib * @arg: arg from ioctl 1295235783Skib * 1296235783Skib * LOCKING: 1297235783Skib * Takes mode config lock. 1298235783Skib * 1299235783Skib * Construct a CRTC configuration structure to return to the user. 1300235783Skib * 1301235783Skib * Called by the user via ioctl. 1302235783Skib * 1303235783Skib * RETURNS: 1304235783Skib * Zero on success, errno on failure. 1305235783Skib */ 1306235783Skibint drm_mode_getcrtc(struct drm_device *dev, 1307235783Skib void *data, struct drm_file *file_priv) 1308235783Skib{ 1309235783Skib struct drm_mode_crtc *crtc_resp = data; 1310235783Skib struct drm_crtc *crtc; 1311235783Skib struct drm_mode_object *obj; 1312235783Skib int ret = 0; 1313235783Skib 1314235783Skib if (!drm_core_check_feature(dev, DRIVER_MODESET)) 1315235783Skib return (EINVAL); 1316235783Skib 1317235783Skib sx_xlock(&dev->mode_config.mutex); 1318235783Skib 1319235783Skib obj = drm_mode_object_find(dev, crtc_resp->crtc_id, 1320235783Skib DRM_MODE_OBJECT_CRTC); 1321235783Skib if (!obj) { 1322235783Skib ret = (EINVAL); 1323235783Skib goto out; 1324235783Skib } 1325235783Skib crtc = obj_to_crtc(obj); 1326235783Skib 1327235783Skib crtc_resp->x = crtc->x; 1328235783Skib crtc_resp->y = crtc->y; 1329235783Skib crtc_resp->gamma_size = crtc->gamma_size; 1330235783Skib if (crtc->fb) 1331235783Skib crtc_resp->fb_id = crtc->fb->base.id; 1332235783Skib else 1333235783Skib crtc_resp->fb_id = 0; 1334235783Skib 1335235783Skib if (crtc->enabled) { 1336235783Skib 1337235783Skib drm_crtc_convert_to_umode(&crtc_resp->mode, &crtc->mode); 1338235783Skib crtc_resp->mode_valid = 1; 1339235783Skib 1340235783Skib } else { 1341235783Skib crtc_resp->mode_valid = 0; 1342235783Skib } 1343235783Skib 1344235783Skibout: 1345235783Skib sx_xunlock(&dev->mode_config.mutex); 1346235783Skib return ret; 1347235783Skib} 1348235783Skib 1349235783Skib/** 1350235783Skib * drm_mode_getconnector - get connector configuration 1351235783Skib * @inode: inode from the ioctl 1352235783Skib * @filp: file * from the ioctl 1353235783Skib * @cmd: cmd from ioctl 1354235783Skib * @arg: arg from ioctl 1355235783Skib * 1356235783Skib * LOCKING: 1357235783Skib * Takes mode config lock. 1358235783Skib * 1359235783Skib * Construct a connector configuration structure to return to the user. 1360235783Skib * 1361235783Skib * Called by the user via ioctl. 1362235783Skib * 1363235783Skib * RETURNS: 1364235783Skib * Zero on success, errno on failure. 1365235783Skib */ 1366235783Skibint drm_mode_getconnector(struct drm_device *dev, void *data, 1367235783Skib struct drm_file *file_priv) 1368235783Skib{ 1369235783Skib struct drm_mode_get_connector *out_resp = data; 1370235783Skib struct drm_mode_object *obj; 1371235783Skib struct drm_connector *connector; 1372235783Skib struct drm_display_mode *mode; 1373235783Skib int mode_count = 0; 1374235783Skib int props_count = 0; 1375235783Skib int encoders_count = 0; 1376235783Skib int ret = 0; 1377235783Skib int copied = 0; 1378235783Skib int i; 1379235783Skib struct drm_mode_modeinfo u_mode; 1380235783Skib struct drm_mode_modeinfo __user *mode_ptr; 1381235783Skib uint32_t *prop_ptr; 1382235783Skib uint64_t *prop_values; 1383235783Skib uint32_t *encoder_ptr; 1384235783Skib 1385235783Skib if (!drm_core_check_feature(dev, DRIVER_MODESET)) 1386235783Skib return (EINVAL); 1387235783Skib 1388235783Skib memset(&u_mode, 0, sizeof(struct drm_mode_modeinfo)); 1389235783Skib 1390235783Skib DRM_DEBUG_KMS("[CONNECTOR:%d:?]\n", out_resp->connector_id); 1391235783Skib 1392235783Skib sx_xlock(&dev->mode_config.mutex); 1393235783Skib 1394235783Skib obj = drm_mode_object_find(dev, out_resp->connector_id, 1395235783Skib DRM_MODE_OBJECT_CONNECTOR); 1396235783Skib if (!obj) { 1397235783Skib ret = EINVAL; 1398235783Skib goto out; 1399235783Skib } 1400235783Skib connector = obj_to_connector(obj); 1401235783Skib 1402235783Skib for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) { 1403235783Skib if (connector->property_ids[i] != 0) { 1404235783Skib props_count++; 1405235783Skib } 1406235783Skib } 1407235783Skib 1408235783Skib for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { 1409235783Skib if (connector->encoder_ids[i] != 0) { 1410235783Skib encoders_count++; 1411235783Skib } 1412235783Skib } 1413235783Skib 1414235783Skib if (out_resp->count_modes == 0) { 1415235783Skib connector->funcs->fill_modes(connector, 1416235783Skib dev->mode_config.max_width, 1417235783Skib dev->mode_config.max_height); 1418235783Skib } 1419235783Skib 1420235783Skib /* delayed so we get modes regardless of pre-fill_modes state */ 1421235783Skib list_for_each_entry(mode, &connector->modes, head) 1422235783Skib mode_count++; 1423235783Skib 1424235783Skib out_resp->connector_id = connector->base.id; 1425235783Skib out_resp->connector_type = connector->connector_type; 1426235783Skib out_resp->connector_type_id = connector->connector_type_id; 1427235783Skib out_resp->mm_width = connector->display_info.width_mm; 1428235783Skib out_resp->mm_height = connector->display_info.height_mm; 1429235783Skib out_resp->subpixel = connector->display_info.subpixel_order; 1430235783Skib out_resp->connection = connector->status; 1431235783Skib if (connector->encoder) 1432235783Skib out_resp->encoder_id = connector->encoder->base.id; 1433235783Skib else 1434235783Skib out_resp->encoder_id = 0; 1435235783Skib 1436235783Skib /* 1437235783Skib * This ioctl is called twice, once to determine how much space is 1438235783Skib * needed, and the 2nd time to fill it. 1439235783Skib */ 1440235783Skib if ((out_resp->count_modes >= mode_count) && mode_count) { 1441235783Skib copied = 0; 1442235783Skib mode_ptr = (struct drm_mode_modeinfo *)(uintptr_t)out_resp->modes_ptr; 1443235783Skib list_for_each_entry(mode, &connector->modes, head) { 1444235783Skib drm_crtc_convert_to_umode(&u_mode, mode); 1445235783Skib if (copyout(&u_mode, mode_ptr + copied, 1446235783Skib sizeof(u_mode))) { 1447235783Skib ret = EFAULT; 1448235783Skib goto out; 1449235783Skib } 1450235783Skib copied++; 1451235783Skib } 1452235783Skib } 1453235783Skib out_resp->count_modes = mode_count; 1454235783Skib 1455235783Skib if ((out_resp->count_props >= props_count) && props_count) { 1456235783Skib copied = 0; 1457235783Skib prop_ptr = (uint32_t *)(uintptr_t)(out_resp->props_ptr); 1458235783Skib prop_values = (uint64_t *)(uintptr_t)(out_resp->prop_values_ptr); 1459235783Skib for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) { 1460235783Skib if (connector->property_ids[i] != 0) { 1461235783Skib if (copyout(&connector->property_ids[i], 1462235783Skib prop_ptr + copied, sizeof(uint32_t))) { 1463235783Skib ret = EFAULT; 1464235783Skib goto out; 1465235783Skib } 1466235783Skib 1467235783Skib if (copyout(&connector->property_values[i], 1468235783Skib prop_values + copied, sizeof(uint64_t))) { 1469235783Skib ret = EFAULT; 1470235783Skib goto out; 1471235783Skib } 1472235783Skib copied++; 1473235783Skib } 1474235783Skib } 1475235783Skib } 1476235783Skib out_resp->count_props = props_count; 1477235783Skib 1478235783Skib if ((out_resp->count_encoders >= encoders_count) && encoders_count) { 1479235783Skib copied = 0; 1480235783Skib encoder_ptr = (uint32_t *)(uintptr_t)(out_resp->encoders_ptr); 1481235783Skib for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { 1482235783Skib if (connector->encoder_ids[i] != 0) { 1483235783Skib if (copyout(&connector->encoder_ids[i], 1484235783Skib encoder_ptr + copied, sizeof(uint32_t))) { 1485235783Skib ret = EFAULT; 1486235783Skib goto out; 1487235783Skib } 1488235783Skib copied++; 1489235783Skib } 1490235783Skib } 1491235783Skib } 1492235783Skib out_resp->count_encoders = encoders_count; 1493235783Skib 1494235783Skibout: 1495235783Skib sx_xunlock(&dev->mode_config.mutex); 1496235783Skib return ret; 1497235783Skib} 1498235783Skib 1499235783Skibint drm_mode_getencoder(struct drm_device *dev, void *data, 1500235783Skib struct drm_file *file_priv) 1501235783Skib{ 1502235783Skib struct drm_mode_get_encoder *enc_resp = data; 1503235783Skib struct drm_mode_object *obj; 1504235783Skib struct drm_encoder *encoder; 1505235783Skib int ret = 0; 1506235783Skib 1507235783Skib if (!drm_core_check_feature(dev, DRIVER_MODESET)) 1508235783Skib return (EINVAL); 1509235783Skib 1510235783Skib sx_xlock(&dev->mode_config.mutex); 1511235783Skib obj = drm_mode_object_find(dev, enc_resp->encoder_id, 1512235783Skib DRM_MODE_OBJECT_ENCODER); 1513235783Skib if (!obj) { 1514235783Skib ret = EINVAL; 1515235783Skib goto out; 1516235783Skib } 1517235783Skib encoder = obj_to_encoder(obj); 1518235783Skib 1519235783Skib if (encoder->crtc) 1520235783Skib enc_resp->crtc_id = encoder->crtc->base.id; 1521235783Skib else 1522235783Skib enc_resp->crtc_id = 0; 1523235783Skib enc_resp->encoder_type = encoder->encoder_type; 1524235783Skib enc_resp->encoder_id = encoder->base.id; 1525235783Skib enc_resp->possible_crtcs = encoder->possible_crtcs; 1526235783Skib enc_resp->possible_clones = encoder->possible_clones; 1527235783Skib 1528235783Skibout: 1529235783Skib sx_xunlock(&dev->mode_config.mutex); 1530235783Skib return ret; 1531235783Skib} 1532235783Skib 1533235783Skib/** 1534235783Skib * drm_mode_getplane_res - get plane info 1535235783Skib * @dev: DRM device 1536235783Skib * @data: ioctl data 1537235783Skib * @file_priv: DRM file info 1538235783Skib * 1539235783Skib * LOCKING: 1540235783Skib * Takes mode config lock. 1541235783Skib * 1542235783Skib * Return an plane count and set of IDs. 1543235783Skib */ 1544235783Skibint drm_mode_getplane_res(struct drm_device *dev, void *data, 1545235783Skib struct drm_file *file_priv) 1546235783Skib{ 1547235783Skib struct drm_mode_get_plane_res *plane_resp = data; 1548235783Skib struct drm_mode_config *config; 1549235783Skib struct drm_plane *plane; 1550235783Skib uint32_t *plane_ptr; 1551235783Skib int copied = 0, ret = 0; 1552235783Skib 1553235783Skib if (!drm_core_check_feature(dev, DRIVER_MODESET)) 1554235783Skib return (EINVAL); 1555235783Skib 1556235783Skib sx_xlock(&dev->mode_config.mutex); 1557235783Skib config = &dev->mode_config; 1558235783Skib 1559235783Skib /* 1560235783Skib * This ioctl is called twice, once to determine how much space is 1561235783Skib * needed, and the 2nd time to fill it. 1562235783Skib */ 1563235783Skib if (config->num_plane && 1564235783Skib (plane_resp->count_planes >= config->num_plane)) { 1565235783Skib plane_ptr = (uint32_t *)(unsigned long)plane_resp->plane_id_ptr; 1566235783Skib 1567235783Skib list_for_each_entry(plane, &config->plane_list, head) { 1568235783Skib if (copyout(&plane->base.id, plane_ptr + copied, 1569235783Skib sizeof(uint32_t))) { 1570235783Skib ret = EFAULT; 1571235783Skib goto out; 1572235783Skib } 1573235783Skib copied++; 1574235783Skib } 1575235783Skib } 1576235783Skib plane_resp->count_planes = config->num_plane; 1577235783Skib 1578235783Skibout: 1579235783Skib sx_xunlock(&dev->mode_config.mutex); 1580235783Skib return ret; 1581235783Skib} 1582235783Skib 1583235783Skib/** 1584235783Skib * drm_mode_getplane - get plane info 1585235783Skib * @dev: DRM device 1586235783Skib * @data: ioctl data 1587235783Skib * @file_priv: DRM file info 1588235783Skib * 1589235783Skib * LOCKING: 1590235783Skib * Takes mode config lock. 1591235783Skib * 1592235783Skib * Return plane info, including formats supported, gamma size, any 1593235783Skib * current fb, etc. 1594235783Skib */ 1595235783Skibint drm_mode_getplane(struct drm_device *dev, void *data, 1596235783Skib struct drm_file *file_priv) 1597235783Skib{ 1598235783Skib struct drm_mode_get_plane *plane_resp = data; 1599235783Skib struct drm_mode_object *obj; 1600235783Skib struct drm_plane *plane; 1601235783Skib uint32_t *format_ptr; 1602235783Skib int ret = 0; 1603235783Skib 1604235783Skib if (!drm_core_check_feature(dev, DRIVER_MODESET)) 1605235783Skib return (EINVAL); 1606235783Skib 1607235783Skib sx_xlock(&dev->mode_config.mutex); 1608235783Skib obj = drm_mode_object_find(dev, plane_resp->plane_id, 1609235783Skib DRM_MODE_OBJECT_PLANE); 1610235783Skib if (!obj) { 1611235783Skib ret = ENOENT; 1612235783Skib goto out; 1613235783Skib } 1614235783Skib plane = obj_to_plane(obj); 1615235783Skib 1616235783Skib if (plane->crtc) 1617235783Skib plane_resp->crtc_id = plane->crtc->base.id; 1618235783Skib else 1619235783Skib plane_resp->crtc_id = 0; 1620235783Skib 1621235783Skib if (plane->fb) 1622235783Skib plane_resp->fb_id = plane->fb->base.id; 1623235783Skib else 1624235783Skib plane_resp->fb_id = 0; 1625235783Skib 1626235783Skib plane_resp->plane_id = plane->base.id; 1627235783Skib plane_resp->possible_crtcs = plane->possible_crtcs; 1628235783Skib plane_resp->gamma_size = plane->gamma_size; 1629235783Skib 1630235783Skib /* 1631235783Skib * This ioctl is called twice, once to determine how much space is 1632235783Skib * needed, and the 2nd time to fill it. 1633235783Skib */ 1634235783Skib if (plane->format_count && 1635235783Skib (plane_resp->count_format_types >= plane->format_count)) { 1636235783Skib format_ptr = (uint32_t *)(unsigned long)plane_resp->format_type_ptr; 1637235783Skib if (copyout(format_ptr, 1638235783Skib plane->format_types, 1639235783Skib sizeof(uint32_t) * plane->format_count)) { 1640235783Skib ret = EFAULT; 1641235783Skib goto out; 1642235783Skib } 1643235783Skib } 1644235783Skib plane_resp->count_format_types = plane->format_count; 1645235783Skib 1646235783Skibout: 1647235783Skib sx_xunlock(&dev->mode_config.mutex); 1648235783Skib return ret; 1649235783Skib} 1650235783Skib 1651235783Skib/** 1652235783Skib * drm_mode_setplane - set up or tear down an plane 1653235783Skib * @dev: DRM device 1654235783Skib * @data: ioctl data* 1655235783Skib * @file_prive: DRM file info 1656235783Skib * 1657235783Skib * LOCKING: 1658235783Skib * Takes mode config lock. 1659235783Skib * 1660235783Skib * Set plane info, including placement, fb, scaling, and other factors. 1661235783Skib * Or pass a NULL fb to disable. 1662235783Skib */ 1663235783Skibint drm_mode_setplane(struct drm_device *dev, void *data, 1664235783Skib struct drm_file *file_priv) 1665235783Skib{ 1666235783Skib struct drm_mode_set_plane *plane_req = data; 1667235783Skib struct drm_mode_object *obj; 1668235783Skib struct drm_plane *plane; 1669235783Skib struct drm_crtc *crtc; 1670235783Skib struct drm_framebuffer *fb; 1671235783Skib int ret = 0; 1672235783Skib unsigned int fb_width, fb_height; 1673235783Skib int i; 1674235783Skib 1675235783Skib if (!drm_core_check_feature(dev, DRIVER_MODESET)) 1676235783Skib return (EINVAL); 1677235783Skib 1678235783Skib sx_xlock(&dev->mode_config.mutex); 1679235783Skib 1680235783Skib /* 1681235783Skib * First, find the plane, crtc, and fb objects. If not available, 1682235783Skib * we don't bother to call the driver. 1683235783Skib */ 1684235783Skib obj = drm_mode_object_find(dev, plane_req->plane_id, 1685235783Skib DRM_MODE_OBJECT_PLANE); 1686235783Skib if (!obj) { 1687235783Skib DRM_DEBUG_KMS("Unknown plane ID %d\n", 1688235783Skib plane_req->plane_id); 1689235783Skib ret = ENOENT; 1690235783Skib goto out; 1691235783Skib } 1692235783Skib plane = obj_to_plane(obj); 1693235783Skib 1694235783Skib /* No fb means shut it down */ 1695235783Skib if (!plane_req->fb_id) { 1696235783Skib plane->funcs->disable_plane(plane); 1697235783Skib plane->crtc = NULL; 1698235783Skib plane->fb = NULL; 1699235783Skib goto out; 1700235783Skib } 1701235783Skib 1702235783Skib obj = drm_mode_object_find(dev, plane_req->crtc_id, 1703235783Skib DRM_MODE_OBJECT_CRTC); 1704235783Skib if (!obj) { 1705235783Skib DRM_DEBUG_KMS("Unknown crtc ID %d\n", 1706235783Skib plane_req->crtc_id); 1707235783Skib ret = ENOENT; 1708235783Skib goto out; 1709235783Skib } 1710235783Skib crtc = obj_to_crtc(obj); 1711235783Skib 1712235783Skib obj = drm_mode_object_find(dev, plane_req->fb_id, 1713235783Skib DRM_MODE_OBJECT_FB); 1714235783Skib if (!obj) { 1715235783Skib DRM_DEBUG_KMS("Unknown framebuffer ID %d\n", 1716235783Skib plane_req->fb_id); 1717235783Skib ret = ENOENT; 1718235783Skib goto out; 1719235783Skib } 1720235783Skib fb = obj_to_fb(obj); 1721235783Skib 1722235783Skib /* Check whether this plane supports the fb pixel format. */ 1723235783Skib for (i = 0; i < plane->format_count; i++) 1724235783Skib if (fb->pixel_format == plane->format_types[i]) 1725235783Skib break; 1726235783Skib if (i == plane->format_count) { 1727235783Skib DRM_DEBUG_KMS("Invalid pixel format 0x%08x\n", fb->pixel_format); 1728235783Skib ret = EINVAL; 1729235783Skib goto out; 1730235783Skib } 1731235783Skib 1732235783Skib fb_width = fb->width << 16; 1733235783Skib fb_height = fb->height << 16; 1734235783Skib 1735235783Skib /* Make sure source coordinates are inside the fb. */ 1736235783Skib if (plane_req->src_w > fb_width || 1737235783Skib plane_req->src_x > fb_width - plane_req->src_w || 1738235783Skib plane_req->src_h > fb_height || 1739235783Skib plane_req->src_y > fb_height - plane_req->src_h) { 1740235783Skib DRM_DEBUG_KMS("Invalid source coordinates " 1741235783Skib "%u.%06ux%u.%06u+%u.%06u+%u.%06u\n", 1742235783Skib plane_req->src_w >> 16, 1743235783Skib ((plane_req->src_w & 0xffff) * 15625) >> 10, 1744235783Skib plane_req->src_h >> 16, 1745235783Skib ((plane_req->src_h & 0xffff) * 15625) >> 10, 1746235783Skib plane_req->src_x >> 16, 1747235783Skib ((plane_req->src_x & 0xffff) * 15625) >> 10, 1748235783Skib plane_req->src_y >> 16, 1749235783Skib ((plane_req->src_y & 0xffff) * 15625) >> 10); 1750235783Skib ret = ENOSPC; 1751235783Skib goto out; 1752235783Skib } 1753235783Skib 1754235783Skib /* Give drivers some help against integer overflows */ 1755235783Skib if (plane_req->crtc_w > INT_MAX || 1756235783Skib plane_req->crtc_x > INT_MAX - (int32_t) plane_req->crtc_w || 1757235783Skib plane_req->crtc_h > INT_MAX || 1758235783Skib plane_req->crtc_y > INT_MAX - (int32_t) plane_req->crtc_h) { 1759235783Skib DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n", 1760235783Skib plane_req->crtc_w, plane_req->crtc_h, 1761235783Skib plane_req->crtc_x, plane_req->crtc_y); 1762235783Skib ret = ERANGE; 1763235783Skib goto out; 1764235783Skib } 1765235783Skib 1766235783Skib ret = -plane->funcs->update_plane(plane, crtc, fb, 1767235783Skib plane_req->crtc_x, plane_req->crtc_y, 1768235783Skib plane_req->crtc_w, plane_req->crtc_h, 1769235783Skib plane_req->src_x, plane_req->src_y, 1770235783Skib plane_req->src_w, plane_req->src_h); 1771235783Skib if (!ret) { 1772235783Skib plane->crtc = crtc; 1773235783Skib plane->fb = fb; 1774235783Skib } 1775235783Skib 1776235783Skibout: 1777235783Skib sx_xunlock(&dev->mode_config.mutex); 1778235783Skib 1779235783Skib return ret; 1780235783Skib} 1781235783Skib 1782235783Skib/** 1783235783Skib * drm_mode_setcrtc - set CRTC configuration 1784235783Skib * @inode: inode from the ioctl 1785235783Skib * @filp: file * from the ioctl 1786235783Skib * @cmd: cmd from ioctl 1787235783Skib * @arg: arg from ioctl 1788235783Skib * 1789235783Skib * LOCKING: 1790235783Skib * Takes mode config lock. 1791235783Skib * 1792235783Skib * Build a new CRTC configuration based on user request. 1793235783Skib * 1794235783Skib * Called by the user via ioctl. 1795235783Skib * 1796235783Skib * RETURNS: 1797235783Skib * Zero on success, errno on failure. 1798235783Skib */ 1799235783Skibint drm_mode_setcrtc(struct drm_device *dev, void *data, 1800235783Skib struct drm_file *file_priv) 1801235783Skib{ 1802235783Skib struct drm_mode_config *config = &dev->mode_config; 1803235783Skib struct drm_mode_crtc *crtc_req = data; 1804235783Skib struct drm_mode_object *obj; 1805235783Skib struct drm_crtc *crtc; 1806235783Skib struct drm_connector **connector_set = NULL, *connector; 1807235783Skib struct drm_framebuffer *fb = NULL; 1808235783Skib struct drm_display_mode *mode = NULL; 1809235783Skib struct drm_mode_set set; 1810235783Skib uint32_t *set_connectors_ptr; 1811235783Skib int ret = 0; 1812235783Skib int i; 1813235783Skib 1814235783Skib if (!drm_core_check_feature(dev, DRIVER_MODESET)) 1815235783Skib return (EINVAL); 1816235783Skib 1817235783Skib /* For some reason crtc x/y offsets are signed internally. */ 1818235783Skib if (crtc_req->x > INT_MAX || crtc_req->y > INT_MAX) 1819235783Skib return (ERANGE); 1820235783Skib 1821235783Skib sx_xlock(&dev->mode_config.mutex); 1822235783Skib obj = drm_mode_object_find(dev, crtc_req->crtc_id, 1823235783Skib DRM_MODE_OBJECT_CRTC); 1824235783Skib if (!obj) { 1825235783Skib DRM_DEBUG_KMS("Unknown CRTC ID %d\n", crtc_req->crtc_id); 1826235783Skib ret = EINVAL; 1827235783Skib goto out; 1828235783Skib } 1829235783Skib crtc = obj_to_crtc(obj); 1830235783Skib DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id); 1831235783Skib 1832235783Skib if (crtc_req->mode_valid) { 1833235783Skib /* If we have a mode we need a framebuffer. */ 1834235783Skib /* If we pass -1, set the mode with the currently bound fb */ 1835235783Skib if (crtc_req->fb_id == -1) { 1836235783Skib if (!crtc->fb) { 1837235783Skib DRM_DEBUG_KMS("CRTC doesn't have current FB\n"); 1838235783Skib ret = -EINVAL; 1839235783Skib goto out; 1840235783Skib } 1841235783Skib fb = crtc->fb; 1842235783Skib } else { 1843235783Skib obj = drm_mode_object_find(dev, crtc_req->fb_id, 1844235783Skib DRM_MODE_OBJECT_FB); 1845235783Skib if (!obj) { 1846235783Skib DRM_DEBUG_KMS("Unknown FB ID%d\n", 1847235783Skib crtc_req->fb_id); 1848235783Skib ret = EINVAL; 1849235783Skib goto out; 1850235783Skib } 1851235783Skib fb = obj_to_fb(obj); 1852235783Skib } 1853235783Skib 1854235783Skib mode = drm_mode_create(dev); 1855235783Skib if (!mode) { 1856235783Skib ret = ENOMEM; 1857235783Skib goto out; 1858235783Skib } 1859235783Skib 1860235783Skib ret = drm_crtc_convert_umode(mode, &crtc_req->mode); 1861235783Skib if (ret) { 1862235783Skib DRM_DEBUG_KMS("Invalid mode\n"); 1863235783Skib goto out; 1864235783Skib } 1865235783Skib 1866235783Skib drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); 1867235783Skib 1868235783Skib if (mode->hdisplay > fb->width || 1869235783Skib mode->vdisplay > fb->height || 1870235783Skib crtc_req->x > fb->width - mode->hdisplay || 1871235783Skib crtc_req->y > fb->height - mode->vdisplay) { 1872235783Skib DRM_DEBUG_KMS("Invalid CRTC viewport %ux%u+%u+%u for fb size %ux%u.\n", 1873235783Skib mode->hdisplay, mode->vdisplay, 1874235783Skib crtc_req->x, crtc_req->y, 1875235783Skib fb->width, fb->height); 1876235783Skib ret = ENOSPC; 1877235783Skib goto out; 1878235783Skib } 1879235783Skib } 1880235783Skib 1881235783Skib if (crtc_req->count_connectors == 0 && mode) { 1882235783Skib DRM_DEBUG_KMS("Count connectors is 0 but mode set\n"); 1883235783Skib ret = EINVAL; 1884235783Skib goto out; 1885235783Skib } 1886235783Skib 1887235783Skib if (crtc_req->count_connectors > 0 && (!mode || !fb)) { 1888235783Skib DRM_DEBUG_KMS("Count connectors is %d but no mode or fb set\n", 1889235783Skib crtc_req->count_connectors); 1890235783Skib ret = EINVAL; 1891235783Skib goto out; 1892235783Skib } 1893235783Skib 1894235783Skib if (crtc_req->count_connectors > 0) { 1895235783Skib u32 out_id; 1896235783Skib 1897235783Skib /* Avoid unbounded kernel memory allocation */ 1898235783Skib if (crtc_req->count_connectors > config->num_connector) { 1899235783Skib ret = EINVAL; 1900235783Skib goto out; 1901235783Skib } 1902235783Skib 1903235783Skib connector_set = malloc(crtc_req->count_connectors * 1904235783Skib sizeof(struct drm_connector *), DRM_MEM_KMS, M_WAITOK); 1905235783Skib 1906235783Skib for (i = 0; i < crtc_req->count_connectors; i++) { 1907235783Skib set_connectors_ptr = (uint32_t *)(uintptr_t)crtc_req->set_connectors_ptr; 1908235783Skib if (copyin(&set_connectors_ptr[i], &out_id, sizeof(uint32_t))) { 1909235783Skib ret = EFAULT; 1910235783Skib goto out; 1911235783Skib } 1912235783Skib 1913235783Skib obj = drm_mode_object_find(dev, out_id, 1914235783Skib DRM_MODE_OBJECT_CONNECTOR); 1915235783Skib if (!obj) { 1916235783Skib DRM_DEBUG_KMS("Connector id %d unknown\n", 1917235783Skib out_id); 1918235783Skib ret = EINVAL; 1919235783Skib goto out; 1920235783Skib } 1921235783Skib connector = obj_to_connector(obj); 1922235783Skib DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", 1923235783Skib connector->base.id, 1924235783Skib drm_get_connector_name(connector)); 1925235783Skib 1926235783Skib connector_set[i] = connector; 1927235783Skib } 1928235783Skib } 1929235783Skib 1930235783Skib set.crtc = crtc; 1931235783Skib set.x = crtc_req->x; 1932235783Skib set.y = crtc_req->y; 1933235783Skib set.mode = mode; 1934235783Skib set.connectors = connector_set; 1935235783Skib set.num_connectors = crtc_req->count_connectors; 1936235783Skib set.fb = fb; 1937235783Skib ret = crtc->funcs->set_config(&set); 1938235783Skib 1939235783Skibout: 1940235783Skib free(connector_set, DRM_MEM_KMS); 1941235783Skib drm_mode_destroy(dev, mode); 1942235783Skib sx_xunlock(&dev->mode_config.mutex); 1943235783Skib return ret; 1944235783Skib} 1945235783Skib 1946235783Skibint drm_mode_cursor_ioctl(struct drm_device *dev, 1947235783Skib void *data, struct drm_file *file_priv) 1948235783Skib{ 1949235783Skib struct drm_mode_cursor *req = data; 1950235783Skib struct drm_mode_object *obj; 1951235783Skib struct drm_crtc *crtc; 1952235783Skib int ret = 0; 1953235783Skib 1954235783Skib if (!drm_core_check_feature(dev, DRIVER_MODESET)) 1955235783Skib return (EINVAL); 1956235783Skib 1957235783Skib if (!req->flags) 1958235783Skib return (EINVAL); 1959235783Skib 1960235783Skib sx_xlock(&dev->mode_config.mutex); 1961235783Skib obj = drm_mode_object_find(dev, req->crtc_id, DRM_MODE_OBJECT_CRTC); 1962235783Skib if (!obj) { 1963235783Skib DRM_DEBUG_KMS("Unknown CRTC ID %d\n", req->crtc_id); 1964235783Skib ret = EINVAL; 1965235783Skib goto out; 1966235783Skib } 1967235783Skib crtc = obj_to_crtc(obj); 1968235783Skib 1969235783Skib if (req->flags & DRM_MODE_CURSOR_BO) { 1970235783Skib if (!crtc->funcs->cursor_set) { 1971235783Skib ret = ENXIO; 1972235783Skib goto out; 1973235783Skib } 1974235783Skib /* Turns off the cursor if handle is 0 */ 1975235783Skib ret = -crtc->funcs->cursor_set(crtc, file_priv, req->handle, 1976235783Skib req->width, req->height); 1977235783Skib } 1978235783Skib 1979235783Skib if (req->flags & DRM_MODE_CURSOR_MOVE) { 1980235783Skib if (crtc->funcs->cursor_move) { 1981235783Skib ret = crtc->funcs->cursor_move(crtc, req->x, req->y); 1982235783Skib } else { 1983235783Skib ret = EFAULT; 1984235783Skib goto out; 1985235783Skib } 1986235783Skib } 1987235783Skibout: 1988235783Skib sx_xunlock(&dev->mode_config.mutex); 1989235783Skib return ret; 1990235783Skib} 1991235783Skib 1992235783Skib/* Original addfb only supported RGB formats, so figure out which one */ 1993235783Skibuint32_t drm_mode_legacy_fb_format(uint32_t bpp, uint32_t depth) 1994235783Skib{ 1995235783Skib uint32_t fmt; 1996235783Skib 1997235783Skib switch (bpp) { 1998235783Skib case 8: 1999235783Skib fmt = DRM_FORMAT_RGB332; 2000235783Skib break; 2001235783Skib case 16: 2002235783Skib if (depth == 15) 2003235783Skib fmt = DRM_FORMAT_XRGB1555; 2004235783Skib else 2005235783Skib fmt = DRM_FORMAT_RGB565; 2006235783Skib break; 2007235783Skib case 24: 2008235783Skib fmt = DRM_FORMAT_RGB888; 2009235783Skib break; 2010235783Skib case 32: 2011235783Skib if (depth == 24) 2012235783Skib fmt = DRM_FORMAT_XRGB8888; 2013235783Skib else if (depth == 30) 2014235783Skib fmt = DRM_FORMAT_XRGB2101010; 2015235783Skib else 2016235783Skib fmt = DRM_FORMAT_ARGB8888; 2017235783Skib break; 2018235783Skib default: 2019235783Skib DRM_ERROR("bad bpp, assuming RGB24 pixel format\n"); 2020235783Skib fmt = DRM_FORMAT_XRGB8888; 2021235783Skib break; 2022235783Skib } 2023235783Skib 2024235783Skib return fmt; 2025235783Skib} 2026235783Skib 2027235783Skib/** 2028235783Skib * drm_mode_addfb - add an FB to the graphics configuration 2029235783Skib * @inode: inode from the ioctl 2030235783Skib * @filp: file * from the ioctl 2031235783Skib * @cmd: cmd from ioctl 2032235783Skib * @arg: arg from ioctl 2033235783Skib * 2034235783Skib * LOCKING: 2035235783Skib * Takes mode config lock. 2036235783Skib * 2037235783Skib * Add a new FB to the specified CRTC, given a user request. 2038235783Skib * 2039235783Skib * Called by the user via ioctl. 2040235783Skib * 2041235783Skib * RETURNS: 2042235783Skib * Zero on success, errno on failure. 2043235783Skib */ 2044235783Skibint drm_mode_addfb(struct drm_device *dev, 2045235783Skib void *data, struct drm_file *file_priv) 2046235783Skib{ 2047235783Skib struct drm_mode_fb_cmd *or = data; 2048235783Skib struct drm_mode_fb_cmd2 r = {}; 2049235783Skib struct drm_mode_config *config = &dev->mode_config; 2050235783Skib struct drm_framebuffer *fb; 2051235783Skib int ret = 0; 2052235783Skib 2053235783Skib /* Use new struct with format internally */ 2054235783Skib r.fb_id = or->fb_id; 2055235783Skib r.width = or->width; 2056235783Skib r.height = or->height; 2057235783Skib r.pitches[0] = or->pitch; 2058235783Skib r.pixel_format = drm_mode_legacy_fb_format(or->bpp, or->depth); 2059235783Skib r.handles[0] = or->handle; 2060235783Skib 2061235783Skib if (!drm_core_check_feature(dev, DRIVER_MODESET)) 2062235783Skib return (EINVAL); 2063235783Skib 2064235783Skib if ((config->min_width > r.width) || (r.width > config->max_width)) 2065235783Skib return (EINVAL); 2066235783Skib if ((config->min_height > r.height) || (r.height > config->max_height)) 2067235783Skib return (EINVAL); 2068235783Skib 2069235783Skib sx_xlock(&dev->mode_config.mutex); 2070235783Skib 2071235783Skib ret = -dev->mode_config.funcs->fb_create(dev, file_priv, &r, &fb); 2072235783Skib if (ret != 0) { 2073235783Skib DRM_ERROR("could not create framebuffer, error %d\n", ret); 2074235783Skib goto out; 2075235783Skib } 2076235783Skib 2077235783Skib or->fb_id = fb->base.id; 2078235783Skib list_add(&fb->filp_head, &file_priv->fbs); 2079235783Skib DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id); 2080235783Skib 2081235783Skibout: 2082235783Skib sx_xunlock(&dev->mode_config.mutex); 2083235783Skib return ret; 2084235783Skib} 2085235783Skib 2086235783Skibstatic int format_check(struct drm_mode_fb_cmd2 *r) 2087235783Skib{ 2088235783Skib uint32_t format = r->pixel_format & ~DRM_FORMAT_BIG_ENDIAN; 2089235783Skib 2090235783Skib switch (format) { 2091235783Skib case DRM_FORMAT_C8: 2092235783Skib case DRM_FORMAT_RGB332: 2093235783Skib case DRM_FORMAT_BGR233: 2094235783Skib case DRM_FORMAT_XRGB4444: 2095235783Skib case DRM_FORMAT_XBGR4444: 2096235783Skib case DRM_FORMAT_RGBX4444: 2097235783Skib case DRM_FORMAT_BGRX4444: 2098235783Skib case DRM_FORMAT_ARGB4444: 2099235783Skib case DRM_FORMAT_ABGR4444: 2100235783Skib case DRM_FORMAT_RGBA4444: 2101235783Skib case DRM_FORMAT_BGRA4444: 2102235783Skib case DRM_FORMAT_XRGB1555: 2103235783Skib case DRM_FORMAT_XBGR1555: 2104235783Skib case DRM_FORMAT_RGBX5551: 2105235783Skib case DRM_FORMAT_BGRX5551: 2106235783Skib case DRM_FORMAT_ARGB1555: 2107235783Skib case DRM_FORMAT_ABGR1555: 2108235783Skib case DRM_FORMAT_RGBA5551: 2109235783Skib case DRM_FORMAT_BGRA5551: 2110235783Skib case DRM_FORMAT_RGB565: 2111235783Skib case DRM_FORMAT_BGR565: 2112235783Skib case DRM_FORMAT_RGB888: 2113235783Skib case DRM_FORMAT_BGR888: 2114235783Skib case DRM_FORMAT_XRGB8888: 2115235783Skib case DRM_FORMAT_XBGR8888: 2116235783Skib case DRM_FORMAT_RGBX8888: 2117235783Skib case DRM_FORMAT_BGRX8888: 2118235783Skib case DRM_FORMAT_ARGB8888: 2119235783Skib case DRM_FORMAT_ABGR8888: 2120235783Skib case DRM_FORMAT_RGBA8888: 2121235783Skib case DRM_FORMAT_BGRA8888: 2122235783Skib case DRM_FORMAT_XRGB2101010: 2123235783Skib case DRM_FORMAT_XBGR2101010: 2124235783Skib case DRM_FORMAT_RGBX1010102: 2125235783Skib case DRM_FORMAT_BGRX1010102: 2126235783Skib case DRM_FORMAT_ARGB2101010: 2127235783Skib case DRM_FORMAT_ABGR2101010: 2128235783Skib case DRM_FORMAT_RGBA1010102: 2129235783Skib case DRM_FORMAT_BGRA1010102: 2130235783Skib case DRM_FORMAT_YUYV: 2131235783Skib case DRM_FORMAT_YVYU: 2132235783Skib case DRM_FORMAT_UYVY: 2133235783Skib case DRM_FORMAT_VYUY: 2134235783Skib case DRM_FORMAT_AYUV: 2135235783Skib case DRM_FORMAT_NV12: 2136235783Skib case DRM_FORMAT_NV21: 2137235783Skib case DRM_FORMAT_NV16: 2138235783Skib case DRM_FORMAT_NV61: 2139235783Skib case DRM_FORMAT_YUV410: 2140235783Skib case DRM_FORMAT_YVU410: 2141235783Skib case DRM_FORMAT_YUV411: 2142235783Skib case DRM_FORMAT_YVU411: 2143235783Skib case DRM_FORMAT_YUV420: 2144235783Skib case DRM_FORMAT_YVU420: 2145235783Skib case DRM_FORMAT_YUV422: 2146235783Skib case DRM_FORMAT_YVU422: 2147235783Skib case DRM_FORMAT_YUV444: 2148235783Skib case DRM_FORMAT_YVU444: 2149235783Skib return 0; 2150235783Skib default: 2151235783Skib return (EINVAL); 2152235783Skib } 2153235783Skib} 2154235783Skib 2155235783Skib/** 2156235783Skib * drm_mode_addfb2 - add an FB to the graphics configuration 2157235783Skib * @inode: inode from the ioctl 2158235783Skib * @filp: file * from the ioctl 2159235783Skib * @cmd: cmd from ioctl 2160235783Skib * @arg: arg from ioctl 2161235783Skib * 2162235783Skib * LOCKING: 2163235783Skib * Takes mode config lock. 2164235783Skib * 2165235783Skib * Add a new FB to the specified CRTC, given a user request with format. 2166235783Skib * 2167235783Skib * Called by the user via ioctl. 2168235783Skib * 2169235783Skib * RETURNS: 2170235783Skib * Zero on success, errno on failure. 2171235783Skib */ 2172235783Skibint drm_mode_addfb2(struct drm_device *dev, 2173235783Skib void *data, struct drm_file *file_priv) 2174235783Skib{ 2175235783Skib struct drm_mode_fb_cmd2 *r = data; 2176235783Skib struct drm_mode_config *config = &dev->mode_config; 2177235783Skib struct drm_framebuffer *fb; 2178235783Skib int ret = 0; 2179235783Skib 2180235783Skib if (!drm_core_check_feature(dev, DRIVER_MODESET)) 2181235783Skib return (EINVAL); 2182235783Skib 2183235783Skib if ((config->min_width > r->width) || (r->width > config->max_width)) { 2184235783Skib DRM_ERROR("bad framebuffer width %d, should be >= %d && <= %d\n", 2185235783Skib r->width, config->min_width, config->max_width); 2186235783Skib return (EINVAL); 2187235783Skib } 2188235783Skib if ((config->min_height > r->height) || (r->height > config->max_height)) { 2189235783Skib DRM_ERROR("bad framebuffer height %d, should be >= %d && <= %d\n", 2190235783Skib r->height, config->min_height, config->max_height); 2191235783Skib return (EINVAL); 2192235783Skib } 2193235783Skib 2194235783Skib ret = format_check(r); 2195235783Skib if (ret) { 2196235783Skib DRM_ERROR("bad framebuffer format 0x%08x\n", r->pixel_format); 2197235783Skib return ret; 2198235783Skib } 2199235783Skib 2200235783Skib sx_xlock(&dev->mode_config.mutex); 2201235783Skib 2202235783Skib /* TODO check buffer is sufficiently large */ 2203235783Skib /* TODO setup destructor callback */ 2204235783Skib 2205235783Skib ret = -dev->mode_config.funcs->fb_create(dev, file_priv, r, &fb); 2206235783Skib if (ret != 0) { 2207235783Skib DRM_ERROR("could not create framebuffer, error %d\n", ret); 2208235783Skib goto out; 2209235783Skib } 2210235783Skib 2211235783Skib r->fb_id = fb->base.id; 2212235783Skib list_add(&fb->filp_head, &file_priv->fbs); 2213235783Skib DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id); 2214235783Skib 2215235783Skibout: 2216235783Skib sx_xunlock(&dev->mode_config.mutex); 2217235783Skib return (ret); 2218235783Skib} 2219235783Skib 2220235783Skib/** 2221235783Skib * drm_mode_rmfb - remove an FB from the configuration 2222235783Skib * @inode: inode from the ioctl 2223235783Skib * @filp: file * from the ioctl 2224235783Skib * @cmd: cmd from ioctl 2225235783Skib * @arg: arg from ioctl 2226235783Skib * 2227235783Skib * LOCKING: 2228235783Skib * Takes mode config lock. 2229235783Skib * 2230235783Skib * Remove the FB specified by the user. 2231235783Skib * 2232235783Skib * Called by the user via ioctl. 2233235783Skib * 2234235783Skib * RETURNS: 2235235783Skib * Zero on success, errno on failure. 2236235783Skib */ 2237235783Skibint drm_mode_rmfb(struct drm_device *dev, 2238235783Skib void *data, struct drm_file *file_priv) 2239235783Skib{ 2240235783Skib struct drm_mode_object *obj; 2241235783Skib struct drm_framebuffer *fb = NULL; 2242235783Skib struct drm_framebuffer *fbl = NULL; 2243235783Skib uint32_t *id = data; 2244235783Skib int ret = 0; 2245235783Skib int found = 0; 2246235783Skib 2247235783Skib if (!drm_core_check_feature(dev, DRIVER_MODESET)) 2248235783Skib return (EINVAL); 2249235783Skib 2250235783Skib sx_xlock(&dev->mode_config.mutex); 2251235783Skib obj = drm_mode_object_find(dev, *id, DRM_MODE_OBJECT_FB); 2252235783Skib /* TODO check that we really get a framebuffer back. */ 2253235783Skib if (!obj) { 2254235783Skib ret = EINVAL; 2255235783Skib goto out; 2256235783Skib } 2257235783Skib fb = obj_to_fb(obj); 2258235783Skib 2259235783Skib list_for_each_entry(fbl, &file_priv->fbs, filp_head) 2260235783Skib if (fb == fbl) 2261235783Skib found = 1; 2262235783Skib 2263235783Skib if (!found) { 2264235783Skib ret = EINVAL; 2265235783Skib goto out; 2266235783Skib } 2267235783Skib 2268235783Skib /* TODO release all crtc connected to the framebuffer */ 2269235783Skib /* TODO unhock the destructor from the buffer object */ 2270235783Skib 2271235783Skib list_del(&fb->filp_head); 2272235783Skib fb->funcs->destroy(fb); 2273235783Skib 2274235783Skibout: 2275235783Skib sx_xunlock(&dev->mode_config.mutex); 2276235783Skib return ret; 2277235783Skib} 2278235783Skib 2279235783Skib/** 2280235783Skib * drm_mode_getfb - get FB info 2281235783Skib * @inode: inode from the ioctl 2282235783Skib * @filp: file * from the ioctl 2283235783Skib * @cmd: cmd from ioctl 2284235783Skib * @arg: arg from ioctl 2285235783Skib * 2286235783Skib * LOCKING: 2287235783Skib * Takes mode config lock. 2288235783Skib * 2289235783Skib * Lookup the FB given its ID and return info about it. 2290235783Skib * 2291235783Skib * Called by the user via ioctl. 2292235783Skib * 2293235783Skib * RETURNS: 2294235783Skib * Zero on success, errno on failure. 2295235783Skib */ 2296235783Skibint drm_mode_getfb(struct drm_device *dev, 2297235783Skib void *data, struct drm_file *file_priv) 2298235783Skib{ 2299235783Skib struct drm_mode_fb_cmd *r = data; 2300235783Skib struct drm_mode_object *obj; 2301235783Skib struct drm_framebuffer *fb; 2302235783Skib int ret = 0; 2303235783Skib 2304235783Skib if (!drm_core_check_feature(dev, DRIVER_MODESET)) 2305235783Skib return (EINVAL); 2306235783Skib 2307235783Skib sx_xlock(&dev->mode_config.mutex); 2308235783Skib obj = drm_mode_object_find(dev, r->fb_id, DRM_MODE_OBJECT_FB); 2309235783Skib if (!obj) { 2310235783Skib ret = EINVAL; 2311235783Skib goto out; 2312235783Skib } 2313235783Skib fb = obj_to_fb(obj); 2314235783Skib 2315235783Skib r->height = fb->height; 2316235783Skib r->width = fb->width; 2317235783Skib r->depth = fb->depth; 2318235783Skib r->bpp = fb->bits_per_pixel; 2319235783Skib r->pitch = fb->pitches[0]; 2320263170Sdumbbell r->handle = 0; 2321235783Skib fb->funcs->create_handle(fb, file_priv, &r->handle); 2322235783Skib 2323235783Skibout: 2324235783Skib sx_xunlock(&dev->mode_config.mutex); 2325235783Skib return ret; 2326235783Skib} 2327235783Skib 2328235783Skibint drm_mode_dirtyfb_ioctl(struct drm_device *dev, 2329235783Skib void *data, struct drm_file *file_priv) 2330235783Skib{ 2331235783Skib struct drm_clip_rect __user *clips_ptr; 2332235783Skib struct drm_clip_rect *clips = NULL; 2333235783Skib struct drm_mode_fb_dirty_cmd *r = data; 2334235783Skib struct drm_mode_object *obj; 2335235783Skib struct drm_framebuffer *fb; 2336235783Skib unsigned flags; 2337235783Skib int num_clips; 2338235783Skib int ret = 0; 2339235783Skib 2340235783Skib if (!drm_core_check_feature(dev, DRIVER_MODESET)) 2341235783Skib return (EINVAL); 2342235783Skib 2343235783Skib sx_xlock(&dev->mode_config.mutex); 2344235783Skib obj = drm_mode_object_find(dev, r->fb_id, DRM_MODE_OBJECT_FB); 2345235783Skib if (!obj) { 2346235783Skib ret = EINVAL; 2347235783Skib goto out_err1; 2348235783Skib } 2349235783Skib fb = obj_to_fb(obj); 2350235783Skib 2351235783Skib num_clips = r->num_clips; 2352235783Skib clips_ptr = (struct drm_clip_rect *)(unsigned long)r->clips_ptr; 2353235783Skib 2354235783Skib if (!num_clips != !clips_ptr) { 2355235783Skib ret = EINVAL; 2356235783Skib goto out_err1; 2357235783Skib } 2358235783Skib 2359235783Skib flags = DRM_MODE_FB_DIRTY_FLAGS & r->flags; 2360235783Skib 2361235783Skib /* If userspace annotates copy, clips must come in pairs */ 2362235783Skib if (flags & DRM_MODE_FB_DIRTY_ANNOTATE_COPY && (num_clips % 2)) { 2363235783Skib ret = EINVAL; 2364235783Skib goto out_err1; 2365235783Skib } 2366235783Skib 2367235783Skib if (num_clips && clips_ptr) { 2368235783Skib if (num_clips < 0 || num_clips > DRM_MODE_FB_DIRTY_MAX_CLIPS) { 2369235783Skib ret = EINVAL; 2370235783Skib goto out_err1; 2371235783Skib } 2372235783Skib clips = malloc(num_clips * sizeof(*clips), DRM_MEM_KMS, 2373235783Skib M_WAITOK | M_ZERO); 2374235783Skib 2375235783Skib ret = copyin(clips_ptr, clips, num_clips * sizeof(*clips)); 2376235783Skib if (ret) 2377235783Skib goto out_err2; 2378235783Skib } 2379235783Skib 2380235783Skib if (fb->funcs->dirty) { 2381235783Skib ret = -fb->funcs->dirty(fb, file_priv, flags, r->color, 2382235783Skib clips, num_clips); 2383235783Skib } else { 2384235783Skib ret = ENOSYS; 2385235783Skib goto out_err2; 2386235783Skib } 2387235783Skib 2388235783Skibout_err2: 2389235783Skib free(clips, DRM_MEM_KMS); 2390235783Skibout_err1: 2391235783Skib sx_xunlock(&dev->mode_config.mutex); 2392235783Skib return ret; 2393235783Skib} 2394235783Skib 2395235783Skib 2396235783Skib/** 2397235783Skib * drm_fb_release - remove and free the FBs on this file 2398235783Skib * @filp: file * from the ioctl 2399235783Skib * 2400235783Skib * LOCKING: 2401235783Skib * Takes mode config lock. 2402235783Skib * 2403235783Skib * Destroy all the FBs associated with @filp. 2404235783Skib * 2405235783Skib * Called by the user via ioctl. 2406235783Skib * 2407235783Skib * RETURNS: 2408235783Skib * Zero on success, errno on failure. 2409235783Skib */ 2410235783Skibvoid drm_fb_release(struct drm_file *priv) 2411235783Skib{ 2412235783Skib#if 1 2413235783Skib struct drm_device *dev = priv->dev; 2414235783Skib#else 2415235783Skib struct drm_device *dev = priv->minor->dev; 2416235783Skib#endif 2417235783Skib struct drm_framebuffer *fb, *tfb; 2418235783Skib 2419235783Skib sx_xlock(&dev->mode_config.mutex); 2420235783Skib list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) { 2421235783Skib list_del(&fb->filp_head); 2422235783Skib fb->funcs->destroy(fb); 2423235783Skib } 2424235783Skib sx_xunlock(&dev->mode_config.mutex); 2425235783Skib} 2426235783Skib 2427235783Skib/** 2428235783Skib * drm_mode_attachmode - add a mode to the user mode list 2429235783Skib * @dev: DRM device 2430235783Skib * @connector: connector to add the mode to 2431235783Skib * @mode: mode to add 2432235783Skib * 2433235783Skib * Add @mode to @connector's user mode list. 2434235783Skib */ 2435235783Skibstatic void drm_mode_attachmode(struct drm_device *dev, 2436235783Skib struct drm_connector *connector, 2437235783Skib struct drm_display_mode *mode) 2438235783Skib{ 2439235783Skib list_add_tail(&mode->head, &connector->user_modes); 2440235783Skib} 2441235783Skib 2442235783Skibint drm_mode_attachmode_crtc(struct drm_device *dev, struct drm_crtc *crtc, 2443235783Skib const struct drm_display_mode *mode) 2444235783Skib{ 2445235783Skib struct drm_connector *connector; 2446235783Skib int ret = 0; 2447235783Skib struct drm_display_mode *dup_mode, *next; 2448235783Skib DRM_LIST_HEAD(list); 2449235783Skib 2450235783Skib list_for_each_entry(connector, &dev->mode_config.connector_list, head) { 2451235783Skib if (!connector->encoder) 2452235783Skib continue; 2453235783Skib if (connector->encoder->crtc == crtc) { 2454235783Skib dup_mode = drm_mode_duplicate(dev, mode); 2455235783Skib if (!dup_mode) { 2456235783Skib ret = ENOMEM; 2457235783Skib goto out; 2458235783Skib } 2459235783Skib list_add_tail(&dup_mode->head, &list); 2460235783Skib } 2461235783Skib } 2462235783Skib 2463235783Skib list_for_each_entry(connector, &dev->mode_config.connector_list, head) { 2464235783Skib if (!connector->encoder) 2465235783Skib continue; 2466235783Skib if (connector->encoder->crtc == crtc) 2467235783Skib list_move_tail(list.next, &connector->user_modes); 2468235783Skib } 2469235783Skib 2470235783Skib MPASS(!list_empty(&list)); 2471235783Skib 2472235783Skib out: 2473235783Skib list_for_each_entry_safe(dup_mode, next, &list, head) 2474235783Skib drm_mode_destroy(dev, dup_mode); 2475235783Skib 2476235783Skib return ret; 2477235783Skib} 2478235783Skib 2479235783Skibstatic int drm_mode_detachmode(struct drm_device *dev, 2480235783Skib struct drm_connector *connector, 2481235783Skib struct drm_display_mode *mode) 2482235783Skib{ 2483235783Skib int found = 0; 2484235783Skib int ret = 0; 2485235783Skib struct drm_display_mode *match_mode, *t; 2486235783Skib 2487235783Skib list_for_each_entry_safe(match_mode, t, &connector->user_modes, head) { 2488235783Skib if (drm_mode_equal(match_mode, mode)) { 2489235783Skib list_del(&match_mode->head); 2490235783Skib drm_mode_destroy(dev, match_mode); 2491235783Skib found = 1; 2492235783Skib break; 2493235783Skib } 2494235783Skib } 2495235783Skib 2496235783Skib if (!found) 2497235783Skib ret = -EINVAL; 2498235783Skib 2499235783Skib return ret; 2500235783Skib} 2501235783Skib 2502235783Skibint drm_mode_detachmode_crtc(struct drm_device *dev, struct drm_display_mode *mode) 2503235783Skib{ 2504235783Skib struct drm_connector *connector; 2505235783Skib 2506235783Skib list_for_each_entry(connector, &dev->mode_config.connector_list, head) { 2507235783Skib drm_mode_detachmode(dev, connector, mode); 2508235783Skib } 2509235783Skib return 0; 2510235783Skib} 2511235783Skib 2512235783Skib/** 2513235783Skib * drm_fb_attachmode - Attach a user mode to an connector 2514235783Skib * @inode: inode from the ioctl 2515235783Skib * @filp: file * from the ioctl 2516235783Skib * @cmd: cmd from ioctl 2517235783Skib * @arg: arg from ioctl 2518235783Skib * 2519235783Skib * This attaches a user specified mode to an connector. 2520235783Skib * Called by the user via ioctl. 2521235783Skib * 2522235783Skib * RETURNS: 2523235783Skib * Zero on success, errno on failure. 2524235783Skib */ 2525235783Skibint drm_mode_attachmode_ioctl(struct drm_device *dev, 2526235783Skib void *data, struct drm_file *file_priv) 2527235783Skib{ 2528235783Skib struct drm_mode_mode_cmd *mode_cmd = data; 2529235783Skib struct drm_connector *connector; 2530235783Skib struct drm_display_mode *mode; 2531235783Skib struct drm_mode_object *obj; 2532235783Skib struct drm_mode_modeinfo *umode = &mode_cmd->mode; 2533235783Skib int ret = 0; 2534235783Skib 2535235783Skib if (!drm_core_check_feature(dev, DRIVER_MODESET)) 2536235783Skib return -EINVAL; 2537235783Skib 2538235783Skib sx_xlock(&dev->mode_config.mutex); 2539235783Skib 2540235783Skib obj = drm_mode_object_find(dev, mode_cmd->connector_id, DRM_MODE_OBJECT_CONNECTOR); 2541235783Skib if (!obj) { 2542235783Skib ret = -EINVAL; 2543235783Skib goto out; 2544235783Skib } 2545235783Skib connector = obj_to_connector(obj); 2546235783Skib 2547235783Skib mode = drm_mode_create(dev); 2548235783Skib if (!mode) { 2549235783Skib ret = -ENOMEM; 2550235783Skib goto out; 2551235783Skib } 2552235783Skib 2553235783Skib ret = drm_crtc_convert_umode(mode, umode); 2554235783Skib if (ret) { 2555235783Skib DRM_DEBUG_KMS("Invalid mode\n"); 2556235783Skib drm_mode_destroy(dev, mode); 2557235783Skib goto out; 2558235783Skib } 2559235783Skib 2560235783Skib drm_mode_attachmode(dev, connector, mode); 2561235783Skibout: 2562235783Skib sx_xunlock(&dev->mode_config.mutex); 2563235783Skib return ret; 2564235783Skib} 2565235783Skib 2566235783Skib 2567235783Skib/** 2568235783Skib * drm_fb_detachmode - Detach a user specified mode from an connector 2569235783Skib * @inode: inode from the ioctl 2570235783Skib * @filp: file * from the ioctl 2571235783Skib * @cmd: cmd from ioctl 2572235783Skib * @arg: arg from ioctl 2573235783Skib * 2574235783Skib * Called by the user via ioctl. 2575235783Skib * 2576235783Skib * RETURNS: 2577235783Skib * Zero on success, errno on failure. 2578235783Skib */ 2579235783Skibint drm_mode_detachmode_ioctl(struct drm_device *dev, 2580235783Skib void *data, struct drm_file *file_priv) 2581235783Skib{ 2582235783Skib struct drm_mode_object *obj; 2583235783Skib struct drm_mode_mode_cmd *mode_cmd = data; 2584235783Skib struct drm_connector *connector; 2585235783Skib struct drm_display_mode mode; 2586235783Skib struct drm_mode_modeinfo *umode = &mode_cmd->mode; 2587235783Skib int ret = 0; 2588235783Skib 2589235783Skib if (!drm_core_check_feature(dev, DRIVER_MODESET)) 2590235783Skib return -EINVAL; 2591235783Skib 2592235783Skib sx_xlock(&dev->mode_config.mutex); 2593235783Skib 2594235783Skib obj = drm_mode_object_find(dev, mode_cmd->connector_id, DRM_MODE_OBJECT_CONNECTOR); 2595235783Skib if (!obj) { 2596235783Skib ret = -EINVAL; 2597235783Skib goto out; 2598235783Skib } 2599235783Skib connector = obj_to_connector(obj); 2600235783Skib 2601235783Skib ret = drm_crtc_convert_umode(&mode, umode); 2602235783Skib if (ret) { 2603235783Skib DRM_DEBUG_KMS("Invalid mode\n"); 2604235783Skib goto out; 2605235783Skib } 2606235783Skib 2607235783Skib ret = drm_mode_detachmode(dev, connector, &mode); 2608235783Skibout: 2609235783Skib sx_xunlock(&dev->mode_config.mutex); 2610235783Skib return ret; 2611235783Skib} 2612235783Skib 2613235783Skibstruct drm_property *drm_property_create(struct drm_device *dev, int flags, 2614235783Skib const char *name, int num_values) 2615235783Skib{ 2616235783Skib struct drm_property *property = NULL; 2617235783Skib int ret; 2618235783Skib 2619235783Skib property = malloc(sizeof(struct drm_property), DRM_MEM_KMS, 2620235783Skib M_WAITOK | M_ZERO); 2621235783Skib 2622235783Skib if (num_values) { 2623235783Skib property->values = malloc(sizeof(uint64_t)*num_values, DRM_MEM_KMS, 2624235783Skib M_WAITOK | M_ZERO); 2625235783Skib } 2626235783Skib 2627235783Skib ret = drm_mode_object_get(dev, &property->base, DRM_MODE_OBJECT_PROPERTY); 2628235783Skib if (ret) 2629235783Skib goto fail; 2630235783Skib property->flags = flags; 2631235783Skib property->num_values = num_values; 2632235783Skib INIT_LIST_HEAD(&property->enum_blob_list); 2633235783Skib 2634235783Skib if (name) { 2635235783Skib strncpy(property->name, name, DRM_PROP_NAME_LEN); 2636235783Skib property->name[DRM_PROP_NAME_LEN-1] = '\0'; 2637235783Skib } 2638235783Skib 2639235783Skib list_add_tail(&property->head, &dev->mode_config.property_list); 2640235783Skib return property; 2641235783Skib 2642235783Skibfail: 2643235783Skib free(property->values, DRM_MEM_KMS); 2644235783Skib free(property, DRM_MEM_KMS); 2645235783Skib return (NULL); 2646235783Skib} 2647235783Skib 2648235783Skibstruct drm_property *drm_property_create_enum(struct drm_device *dev, int flags, 2649235783Skib const char *name, 2650235783Skib const struct drm_prop_enum_list *props, 2651235783Skib int num_values) 2652235783Skib{ 2653235783Skib struct drm_property *property; 2654235783Skib int i, ret; 2655235783Skib 2656235783Skib flags |= DRM_MODE_PROP_ENUM; 2657235783Skib 2658235783Skib property = drm_property_create(dev, flags, name, num_values); 2659235783Skib if (!property) 2660235783Skib return NULL; 2661235783Skib 2662235783Skib for (i = 0; i < num_values; i++) { 2663235783Skib ret = drm_property_add_enum(property, i, 2664235783Skib props[i].type, 2665235783Skib props[i].name); 2666235783Skib if (ret) { 2667235783Skib drm_property_destroy(dev, property); 2668235783Skib return NULL; 2669235783Skib } 2670235783Skib } 2671235783Skib 2672235783Skib return property; 2673235783Skib} 2674235783Skib 2675235783Skibstruct drm_property *drm_property_create_range(struct drm_device *dev, int flags, 2676235783Skib const char *name, 2677235783Skib uint64_t min, uint64_t max) 2678235783Skib{ 2679235783Skib struct drm_property *property; 2680235783Skib 2681235783Skib flags |= DRM_MODE_PROP_RANGE; 2682235783Skib 2683235783Skib property = drm_property_create(dev, flags, name, 2); 2684235783Skib if (!property) 2685235783Skib return NULL; 2686235783Skib 2687235783Skib property->values[0] = min; 2688235783Skib property->values[1] = max; 2689235783Skib 2690235783Skib return property; 2691235783Skib} 2692235783Skib 2693235783Skibint drm_property_add_enum(struct drm_property *property, int index, 2694235783Skib uint64_t value, const char *name) 2695235783Skib{ 2696235783Skib struct drm_property_enum *prop_enum; 2697235783Skib 2698235783Skib if (!(property->flags & DRM_MODE_PROP_ENUM)) 2699235783Skib return -EINVAL; 2700235783Skib 2701235783Skib if (!list_empty(&property->enum_blob_list)) { 2702235783Skib list_for_each_entry(prop_enum, &property->enum_blob_list, head) { 2703235783Skib if (prop_enum->value == value) { 2704235783Skib strncpy(prop_enum->name, name, DRM_PROP_NAME_LEN); 2705235783Skib prop_enum->name[DRM_PROP_NAME_LEN-1] = '\0'; 2706235783Skib return 0; 2707235783Skib } 2708235783Skib } 2709235783Skib } 2710235783Skib 2711235783Skib prop_enum = malloc(sizeof(struct drm_property_enum), DRM_MEM_KMS, 2712235783Skib M_WAITOK | M_ZERO); 2713235783Skib 2714235783Skib strncpy(prop_enum->name, name, DRM_PROP_NAME_LEN); 2715235783Skib prop_enum->name[DRM_PROP_NAME_LEN-1] = '\0'; 2716235783Skib prop_enum->value = value; 2717235783Skib 2718235783Skib property->values[index] = value; 2719235783Skib list_add_tail(&prop_enum->head, &property->enum_blob_list); 2720235783Skib return 0; 2721235783Skib} 2722235783Skib 2723235783Skibvoid drm_property_destroy(struct drm_device *dev, struct drm_property *property) 2724235783Skib{ 2725235783Skib struct drm_property_enum *prop_enum, *pt; 2726235783Skib 2727235783Skib list_for_each_entry_safe(prop_enum, pt, &property->enum_blob_list, head) { 2728235783Skib list_del(&prop_enum->head); 2729235783Skib free(prop_enum, DRM_MEM_KMS); 2730235783Skib } 2731235783Skib 2732235783Skib if (property->num_values) 2733235783Skib free(property->values, DRM_MEM_KMS); 2734235783Skib drm_mode_object_put(dev, &property->base); 2735235783Skib list_del(&property->head); 2736235783Skib free(property, DRM_MEM_KMS); 2737235783Skib} 2738235783Skib 2739235783Skibint drm_connector_attach_property(struct drm_connector *connector, 2740235783Skib struct drm_property *property, uint64_t init_val) 2741235783Skib{ 2742235783Skib int i; 2743235783Skib 2744235783Skib for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) { 2745235783Skib if (connector->property_ids[i] == 0) { 2746235783Skib connector->property_ids[i] = property->base.id; 2747235783Skib connector->property_values[i] = init_val; 2748235783Skib break; 2749235783Skib } 2750235783Skib } 2751235783Skib 2752235783Skib if (i == DRM_CONNECTOR_MAX_PROPERTY) 2753235783Skib return -EINVAL; 2754235783Skib return 0; 2755235783Skib} 2756235783Skib 2757235783Skibint drm_connector_property_set_value(struct drm_connector *connector, 2758235783Skib struct drm_property *property, uint64_t value) 2759235783Skib{ 2760235783Skib int i; 2761235783Skib 2762235783Skib for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) { 2763235783Skib if (connector->property_ids[i] == property->base.id) { 2764235783Skib connector->property_values[i] = value; 2765235783Skib break; 2766235783Skib } 2767235783Skib } 2768235783Skib 2769235783Skib if (i == DRM_CONNECTOR_MAX_PROPERTY) 2770235783Skib return -EINVAL; 2771235783Skib return 0; 2772235783Skib} 2773235783Skib 2774235783Skibint drm_connector_property_get_value(struct drm_connector *connector, 2775235783Skib struct drm_property *property, uint64_t *val) 2776235783Skib{ 2777235783Skib int i; 2778235783Skib 2779235783Skib for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) { 2780235783Skib if (connector->property_ids[i] == property->base.id) { 2781235783Skib *val = connector->property_values[i]; 2782235783Skib break; 2783235783Skib } 2784235783Skib } 2785235783Skib 2786235783Skib if (i == DRM_CONNECTOR_MAX_PROPERTY) 2787235783Skib return -EINVAL; 2788235783Skib return 0; 2789235783Skib} 2790235783Skib 2791235783Skibint drm_mode_getproperty_ioctl(struct drm_device *dev, 2792235783Skib void *data, struct drm_file *file_priv) 2793235783Skib{ 2794235783Skib struct drm_mode_object *obj; 2795235783Skib struct drm_mode_get_property *out_resp = data; 2796235783Skib struct drm_property *property; 2797235783Skib int enum_count = 0; 2798235783Skib int blob_count = 0; 2799235783Skib int value_count = 0; 2800235783Skib int ret = 0, i; 2801235783Skib int copied; 2802235783Skib struct drm_property_enum *prop_enum; 2803235783Skib struct drm_mode_property_enum __user *enum_ptr; 2804235783Skib struct drm_property_blob *prop_blob; 2805235783Skib uint32_t *blob_id_ptr; 2806235783Skib uint64_t *values_ptr; 2807235783Skib uint32_t *blob_length_ptr; 2808235783Skib 2809235783Skib if (!drm_core_check_feature(dev, DRIVER_MODESET)) 2810235783Skib return -EINVAL; 2811235783Skib 2812235783Skib sx_xlock(&dev->mode_config.mutex); 2813235783Skib obj = drm_mode_object_find(dev, out_resp->prop_id, DRM_MODE_OBJECT_PROPERTY); 2814235783Skib if (!obj) { 2815235783Skib ret = -EINVAL; 2816235783Skib goto done; 2817235783Skib } 2818235783Skib property = obj_to_property(obj); 2819235783Skib 2820235783Skib if (property->flags & DRM_MODE_PROP_ENUM) { 2821235783Skib list_for_each_entry(prop_enum, &property->enum_blob_list, head) 2822235783Skib enum_count++; 2823235783Skib } else if (property->flags & DRM_MODE_PROP_BLOB) { 2824235783Skib list_for_each_entry(prop_blob, &property->enum_blob_list, head) 2825235783Skib blob_count++; 2826235783Skib } 2827235783Skib 2828235783Skib value_count = property->num_values; 2829235783Skib 2830235783Skib strncpy(out_resp->name, property->name, DRM_PROP_NAME_LEN); 2831235783Skib out_resp->name[DRM_PROP_NAME_LEN-1] = 0; 2832235783Skib out_resp->flags = property->flags; 2833235783Skib 2834235783Skib if ((out_resp->count_values >= value_count) && value_count) { 2835235783Skib values_ptr = (uint64_t *)(uintptr_t)out_resp->values_ptr; 2836235783Skib for (i = 0; i < value_count; i++) { 2837235783Skib if (copyout(&property->values[i], values_ptr + i, sizeof(uint64_t))) { 2838235783Skib ret = -EFAULT; 2839235783Skib goto done; 2840235783Skib } 2841235783Skib } 2842235783Skib } 2843235783Skib out_resp->count_values = value_count; 2844235783Skib 2845235783Skib if (property->flags & DRM_MODE_PROP_ENUM) { 2846235783Skib if ((out_resp->count_enum_blobs >= enum_count) && enum_count) { 2847235783Skib copied = 0; 2848235783Skib enum_ptr = (struct drm_mode_property_enum *)(uintptr_t)out_resp->enum_blob_ptr; 2849235783Skib list_for_each_entry(prop_enum, &property->enum_blob_list, head) { 2850235783Skib 2851235783Skib if (copyout(&prop_enum->value, &enum_ptr[copied].value, sizeof(uint64_t))) { 2852235783Skib ret = -EFAULT; 2853235783Skib goto done; 2854235783Skib } 2855235783Skib 2856235783Skib if (copyout(&prop_enum->name, 2857235783Skib &enum_ptr[copied].name,DRM_PROP_NAME_LEN)) { 2858235783Skib ret = -EFAULT; 2859235783Skib goto done; 2860235783Skib } 2861235783Skib copied++; 2862235783Skib } 2863235783Skib } 2864235783Skib out_resp->count_enum_blobs = enum_count; 2865235783Skib } 2866235783Skib 2867235783Skib if (property->flags & DRM_MODE_PROP_BLOB) { 2868235783Skib if ((out_resp->count_enum_blobs >= blob_count) && blob_count) { 2869235783Skib copied = 0; 2870235783Skib blob_id_ptr = (uint32_t *)(uintptr_t)out_resp->enum_blob_ptr; 2871235783Skib blob_length_ptr = (uint32_t *)(uintptr_t)out_resp->values_ptr; 2872235783Skib 2873235783Skib list_for_each_entry(prop_blob, &property->enum_blob_list, head) { 2874235783Skib if (copyout(&prop_blob->base.id, 2875235783Skib blob_id_ptr + copied, sizeof(uint32_t))) { 2876235783Skib ret = -EFAULT; 2877235783Skib goto done; 2878235783Skib } 2879235783Skib 2880235783Skib if (copyout(&prop_blob->length, 2881235783Skib blob_length_ptr + copied, sizeof(uint32_t))) { 2882235783Skib ret = -EFAULT; 2883235783Skib goto done; 2884235783Skib } 2885235783Skib 2886235783Skib copied++; 2887235783Skib } 2888235783Skib } 2889235783Skib out_resp->count_enum_blobs = blob_count; 2890235783Skib } 2891235783Skibdone: 2892235783Skib sx_xunlock(&dev->mode_config.mutex); 2893235783Skib return ret; 2894235783Skib} 2895235783Skib 2896235783Skibstatic struct drm_property_blob *drm_property_create_blob(struct drm_device *dev, int length, 2897235783Skib void *data) 2898235783Skib{ 2899235783Skib struct drm_property_blob *blob; 2900235783Skib int ret; 2901235783Skib 2902235783Skib if (!length || !data) 2903235783Skib return NULL; 2904235783Skib 2905235783Skib blob = malloc(sizeof(struct drm_property_blob) + length, DRM_MEM_KMS, 2906235783Skib M_WAITOK | M_ZERO); 2907235783Skib 2908235783Skib ret = drm_mode_object_get(dev, &blob->base, DRM_MODE_OBJECT_BLOB); 2909235783Skib if (ret) { 2910235783Skib free(blob, DRM_MEM_KMS); 2911235783Skib return (NULL); 2912235783Skib } 2913235783Skib 2914235783Skib blob->length = length; 2915235783Skib 2916235783Skib memcpy(blob->data, data, length); 2917235783Skib 2918235783Skib list_add_tail(&blob->head, &dev->mode_config.property_blob_list); 2919235783Skib return blob; 2920235783Skib} 2921235783Skib 2922235783Skibstatic void drm_property_destroy_blob(struct drm_device *dev, 2923235783Skib struct drm_property_blob *blob) 2924235783Skib{ 2925235783Skib drm_mode_object_put(dev, &blob->base); 2926235783Skib list_del(&blob->head); 2927235783Skib free(blob, DRM_MEM_KMS); 2928235783Skib} 2929235783Skib 2930235783Skibint drm_mode_getblob_ioctl(struct drm_device *dev, 2931235783Skib void *data, struct drm_file *file_priv) 2932235783Skib{ 2933235783Skib struct drm_mode_object *obj; 2934235783Skib struct drm_mode_get_blob *out_resp = data; 2935235783Skib struct drm_property_blob *blob; 2936235783Skib int ret = 0; 2937235783Skib void *blob_ptr; 2938235783Skib 2939235783Skib if (!drm_core_check_feature(dev, DRIVER_MODESET)) 2940235783Skib return -EINVAL; 2941235783Skib 2942235783Skib sx_xlock(&dev->mode_config.mutex); 2943235783Skib obj = drm_mode_object_find(dev, out_resp->blob_id, DRM_MODE_OBJECT_BLOB); 2944235783Skib if (!obj) { 2945235783Skib ret = -EINVAL; 2946235783Skib goto done; 2947235783Skib } 2948235783Skib blob = obj_to_blob(obj); 2949235783Skib 2950235783Skib if (out_resp->length == blob->length) { 2951235783Skib blob_ptr = (void *)(unsigned long)out_resp->data; 2952235783Skib if (copyout(blob->data, blob_ptr, blob->length)){ 2953235783Skib ret = -EFAULT; 2954235783Skib goto done; 2955235783Skib } 2956235783Skib } 2957235783Skib out_resp->length = blob->length; 2958235783Skib 2959235783Skibdone: 2960235783Skib sx_xunlock(&dev->mode_config.mutex); 2961235783Skib return ret; 2962235783Skib} 2963235783Skib 2964235783Skibint drm_mode_connector_update_edid_property(struct drm_connector *connector, 2965235783Skib struct edid *edid) 2966235783Skib{ 2967235783Skib struct drm_device *dev = connector->dev; 2968235783Skib int ret = 0, size; 2969235783Skib 2970235783Skib if (connector->edid_blob_ptr) 2971235783Skib drm_property_destroy_blob(dev, connector->edid_blob_ptr); 2972235783Skib 2973235783Skib /* Delete edid, when there is none. */ 2974235783Skib if (!edid) { 2975235783Skib connector->edid_blob_ptr = NULL; 2976235783Skib ret = drm_connector_property_set_value(connector, dev->mode_config.edid_property, 0); 2977235783Skib return ret; 2978235783Skib } 2979235783Skib 2980235783Skib size = EDID_LENGTH * (1 + edid->extensions); 2981235783Skib connector->edid_blob_ptr = drm_property_create_blob(connector->dev, 2982235783Skib size, edid); 2983235783Skib 2984235783Skib ret = drm_connector_property_set_value(connector, 2985235783Skib dev->mode_config.edid_property, 2986235783Skib connector->edid_blob_ptr->base.id); 2987235783Skib 2988235783Skib return ret; 2989235783Skib} 2990235783Skib 2991235783Skibint drm_mode_connector_property_set_ioctl(struct drm_device *dev, 2992235783Skib void *data, struct drm_file *file_priv) 2993235783Skib{ 2994235783Skib struct drm_mode_connector_set_property *out_resp = data; 2995235783Skib struct drm_mode_object *obj; 2996235783Skib struct drm_property *property; 2997235783Skib struct drm_connector *connector; 2998235783Skib int ret = -EINVAL; 2999235783Skib int i; 3000235783Skib 3001235783Skib if (!drm_core_check_feature(dev, DRIVER_MODESET)) 3002235783Skib return -EINVAL; 3003235783Skib 3004235783Skib sx_xlock(&dev->mode_config.mutex); 3005235783Skib 3006235783Skib obj = drm_mode_object_find(dev, out_resp->connector_id, DRM_MODE_OBJECT_CONNECTOR); 3007235783Skib if (!obj) { 3008235783Skib goto out; 3009235783Skib } 3010235783Skib connector = obj_to_connector(obj); 3011235783Skib 3012235783Skib for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) { 3013235783Skib if (connector->property_ids[i] == out_resp->prop_id) 3014235783Skib break; 3015235783Skib } 3016235783Skib 3017235783Skib if (i == DRM_CONNECTOR_MAX_PROPERTY) { 3018235783Skib goto out; 3019235783Skib } 3020235783Skib 3021235783Skib obj = drm_mode_object_find(dev, out_resp->prop_id, DRM_MODE_OBJECT_PROPERTY); 3022235783Skib if (!obj) { 3023235783Skib goto out; 3024235783Skib } 3025235783Skib property = obj_to_property(obj); 3026235783Skib 3027235783Skib if (property->flags & DRM_MODE_PROP_IMMUTABLE) 3028235783Skib goto out; 3029235783Skib 3030235783Skib if (property->flags & DRM_MODE_PROP_RANGE) { 3031235783Skib if (out_resp->value < property->values[0]) 3032235783Skib goto out; 3033235783Skib 3034235783Skib if (out_resp->value > property->values[1]) 3035235783Skib goto out; 3036235783Skib } else { 3037235783Skib int found = 0; 3038235783Skib for (i = 0; i < property->num_values; i++) { 3039235783Skib if (property->values[i] == out_resp->value) { 3040235783Skib found = 1; 3041235783Skib break; 3042235783Skib } 3043235783Skib } 3044235783Skib if (!found) { 3045235783Skib goto out; 3046235783Skib } 3047235783Skib } 3048235783Skib 3049235783Skib /* Do DPMS ourselves */ 3050235783Skib if (property == connector->dev->mode_config.dpms_property) { 3051235783Skib if (connector->funcs->dpms) 3052235783Skib (*connector->funcs->dpms)(connector, (int) out_resp->value); 3053235783Skib ret = 0; 3054235783Skib } else if (connector->funcs->set_property) 3055235783Skib ret = connector->funcs->set_property(connector, property, out_resp->value); 3056235783Skib 3057235783Skib /* store the property value if successful */ 3058235783Skib if (!ret) 3059235783Skib drm_connector_property_set_value(connector, property, out_resp->value); 3060235783Skibout: 3061235783Skib sx_xunlock(&dev->mode_config.mutex); 3062235783Skib return ret; 3063235783Skib} 3064235783Skib 3065235783Skibint drm_mode_connector_attach_encoder(struct drm_connector *connector, 3066235783Skib struct drm_encoder *encoder) 3067235783Skib{ 3068235783Skib int i; 3069235783Skib 3070235783Skib for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { 3071235783Skib if (connector->encoder_ids[i] == 0) { 3072235783Skib connector->encoder_ids[i] = encoder->base.id; 3073235783Skib return 0; 3074235783Skib } 3075235783Skib } 3076235783Skib return -ENOMEM; 3077235783Skib} 3078235783Skib 3079235783Skibvoid drm_mode_connector_detach_encoder(struct drm_connector *connector, 3080235783Skib struct drm_encoder *encoder) 3081235783Skib{ 3082235783Skib int i; 3083235783Skib for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { 3084235783Skib if (connector->encoder_ids[i] == encoder->base.id) { 3085235783Skib connector->encoder_ids[i] = 0; 3086235783Skib if (connector->encoder == encoder) 3087235783Skib connector->encoder = NULL; 3088235783Skib break; 3089235783Skib } 3090235783Skib } 3091235783Skib} 3092235783Skib 3093235783Skibint drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc, 3094235783Skib int gamma_size) 3095235783Skib{ 3096235783Skib crtc->gamma_size = gamma_size; 3097235783Skib 3098235783Skib crtc->gamma_store = malloc(gamma_size * sizeof(uint16_t) * 3, 3099235783Skib DRM_MEM_KMS, M_WAITOK | M_ZERO); 3100235783Skib 3101235783Skib return 0; 3102235783Skib} 3103235783Skib 3104235783Skibint drm_mode_gamma_set_ioctl(struct drm_device *dev, 3105235783Skib void *data, struct drm_file *file_priv) 3106235783Skib{ 3107235783Skib struct drm_mode_crtc_lut *crtc_lut = data; 3108235783Skib struct drm_mode_object *obj; 3109235783Skib struct drm_crtc *crtc; 3110235783Skib void *r_base, *g_base, *b_base; 3111235783Skib int size; 3112235783Skib int ret = 0; 3113235783Skib 3114235783Skib if (!drm_core_check_feature(dev, DRIVER_MODESET)) 3115235783Skib return -EINVAL; 3116235783Skib 3117235783Skib sx_xlock(&dev->mode_config.mutex); 3118235783Skib obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC); 3119235783Skib if (!obj) { 3120235783Skib ret = -EINVAL; 3121235783Skib goto out; 3122235783Skib } 3123235783Skib crtc = obj_to_crtc(obj); 3124235783Skib 3125235783Skib /* memcpy into gamma store */ 3126235783Skib if (crtc_lut->gamma_size != crtc->gamma_size) { 3127235783Skib ret = -EINVAL; 3128235783Skib goto out; 3129235783Skib } 3130235783Skib 3131235783Skib size = crtc_lut->gamma_size * (sizeof(uint16_t)); 3132235783Skib r_base = crtc->gamma_store; 3133235783Skib if (copyin((void *)(uintptr_t)crtc_lut->red, r_base, size)) { 3134235783Skib ret = -EFAULT; 3135235783Skib goto out; 3136235783Skib } 3137235783Skib 3138235783Skib g_base = (char *)r_base + size; 3139235783Skib if (copyin((void *)(uintptr_t)crtc_lut->green, g_base, size)) { 3140235783Skib ret = -EFAULT; 3141235783Skib goto out; 3142235783Skib } 3143235783Skib 3144235783Skib b_base = (char *)g_base + size; 3145235783Skib if (copyin((void *)(uintptr_t)crtc_lut->blue, b_base, size)) { 3146235783Skib ret = -EFAULT; 3147235783Skib goto out; 3148235783Skib } 3149235783Skib 3150235783Skib crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, 0, crtc->gamma_size); 3151235783Skib 3152235783Skibout: 3153235783Skib sx_xunlock(&dev->mode_config.mutex); 3154235783Skib return ret; 3155235783Skib 3156235783Skib} 3157235783Skib 3158235783Skibint drm_mode_gamma_get_ioctl(struct drm_device *dev, 3159235783Skib void *data, struct drm_file *file_priv) 3160235783Skib{ 3161235783Skib struct drm_mode_crtc_lut *crtc_lut = data; 3162235783Skib struct drm_mode_object *obj; 3163235783Skib struct drm_crtc *crtc; 3164235783Skib void *r_base, *g_base, *b_base; 3165235783Skib int size; 3166235783Skib int ret = 0; 3167235783Skib 3168235783Skib if (!drm_core_check_feature(dev, DRIVER_MODESET)) 3169235783Skib return -EINVAL; 3170235783Skib 3171235783Skib sx_xlock(&dev->mode_config.mutex); 3172235783Skib obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC); 3173235783Skib if (!obj) { 3174235783Skib ret = -EINVAL; 3175235783Skib goto out; 3176235783Skib } 3177235783Skib crtc = obj_to_crtc(obj); 3178235783Skib 3179235783Skib /* memcpy into gamma store */ 3180235783Skib if (crtc_lut->gamma_size != crtc->gamma_size) { 3181235783Skib ret = -EINVAL; 3182235783Skib goto out; 3183235783Skib } 3184235783Skib 3185235783Skib size = crtc_lut->gamma_size * (sizeof(uint16_t)); 3186235783Skib r_base = crtc->gamma_store; 3187235783Skib if (copyout(r_base, (void *)(uintptr_t)crtc_lut->red, size)) { 3188235783Skib ret = -EFAULT; 3189235783Skib goto out; 3190235783Skib } 3191235783Skib 3192235783Skib g_base = (char *)r_base + size; 3193235783Skib if (copyout(g_base, (void *)(uintptr_t)crtc_lut->green, size)) { 3194235783Skib ret = -EFAULT; 3195235783Skib goto out; 3196235783Skib } 3197235783Skib 3198235783Skib b_base = (char *)g_base + size; 3199235783Skib if (copyout(b_base, (void *)(uintptr_t)crtc_lut->blue, size)) { 3200235783Skib ret = -EFAULT; 3201235783Skib goto out; 3202235783Skib } 3203235783Skibout: 3204235783Skib sx_xunlock(&dev->mode_config.mutex); 3205235783Skib return ret; 3206235783Skib} 3207235783Skib 3208235783Skibstatic void 3209235783Skibdrm_kms_free(void *arg) 3210235783Skib{ 3211235783Skib 3212235783Skib free(arg, DRM_MEM_KMS); 3213235783Skib} 3214235783Skib 3215235783Skibint drm_mode_page_flip_ioctl(struct drm_device *dev, void *data, 3216235783Skib struct drm_file *file_priv) 3217235783Skib{ 3218235783Skib struct drm_mode_crtc_page_flip *page_flip = data; 3219235783Skib struct drm_mode_object *obj; 3220235783Skib struct drm_crtc *crtc; 3221235783Skib struct drm_framebuffer *fb; 3222235783Skib struct drm_pending_vblank_event *e = NULL; 3223235783Skib int ret = EINVAL; 3224235783Skib 3225235783Skib if (page_flip->flags & ~DRM_MODE_PAGE_FLIP_FLAGS || 3226235783Skib page_flip->reserved != 0) 3227235783Skib return (EINVAL); 3228235783Skib 3229235783Skib sx_xlock(&dev->mode_config.mutex); 3230235783Skib obj = drm_mode_object_find(dev, page_flip->crtc_id, DRM_MODE_OBJECT_CRTC); 3231235783Skib if (!obj) 3232235783Skib goto out; 3233235783Skib crtc = obj_to_crtc(obj); 3234235783Skib 3235235783Skib if (crtc->fb == NULL) { 3236235783Skib /* The framebuffer is currently unbound, presumably 3237235783Skib * due to a hotplug event, that userspace has not 3238235783Skib * yet discovered. 3239235783Skib */ 3240235783Skib ret = EBUSY; 3241235783Skib goto out; 3242235783Skib } 3243235783Skib 3244235783Skib if (crtc->funcs->page_flip == NULL) 3245235783Skib goto out; 3246235783Skib 3247235783Skib obj = drm_mode_object_find(dev, page_flip->fb_id, DRM_MODE_OBJECT_FB); 3248235783Skib if (!obj) 3249235783Skib goto out; 3250235783Skib fb = obj_to_fb(obj); 3251235783Skib 3252235783Skib if (crtc->mode.hdisplay > fb->width || 3253235783Skib crtc->mode.vdisplay > fb->height || 3254235783Skib crtc->x > fb->width - crtc->mode.hdisplay || 3255235783Skib crtc->y > fb->height - crtc->mode.vdisplay) { 3256235783Skib DRM_DEBUG_KMS("Invalid fb size %ux%u for CRTC viewport %ux%u+%d+%d.\n", 3257235783Skib fb->width, fb->height, 3258235783Skib crtc->mode.hdisplay, crtc->mode.vdisplay, 3259235783Skib crtc->x, crtc->y); 3260235783Skib ret = ENOSPC; 3261235783Skib goto out; 3262235783Skib } 3263235783Skib 3264235783Skib if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) { 3265235783Skib ret = ENOMEM; 3266235783Skib mtx_lock(&dev->event_lock); 3267235783Skib if (file_priv->event_space < sizeof e->event) { 3268235783Skib mtx_unlock(&dev->event_lock); 3269235783Skib goto out; 3270235783Skib } 3271235783Skib file_priv->event_space -= sizeof e->event; 3272235783Skib mtx_unlock(&dev->event_lock); 3273235783Skib 3274235783Skib e = malloc(sizeof *e, DRM_MEM_KMS, M_WAITOK | M_ZERO); 3275235783Skib 3276235783Skib e->event.base.type = DRM_EVENT_FLIP_COMPLETE; 3277235783Skib e->event.base.length = sizeof e->event; 3278235783Skib e->event.user_data = page_flip->user_data; 3279235783Skib e->base.event = &e->event.base; 3280235783Skib e->base.file_priv = file_priv; 3281235783Skib e->base.destroy = 3282235783Skib (void (*) (struct drm_pending_event *))drm_kms_free; 3283235783Skib } 3284235783Skib 3285235783Skib ret = -crtc->funcs->page_flip(crtc, fb, e); 3286235783Skib if (ret != 0) { 3287235783Skib if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) { 3288235783Skib mtx_lock(&dev->event_lock); 3289235783Skib file_priv->event_space += sizeof e->event; 3290235783Skib mtx_unlock(&dev->event_lock); 3291235783Skib free(e, DRM_MEM_KMS); 3292235783Skib } 3293235783Skib } 3294235783Skib 3295235783Skibout: 3296235783Skib sx_xunlock(&dev->mode_config.mutex); 3297235783Skib CTR3(KTR_DRM, "page_flip_ioctl %d %d %d", curproc->p_pid, 3298235783Skib page_flip->crtc_id, ret); 3299235783Skib return (ret); 3300235783Skib} 3301235783Skib 3302235783Skibvoid drm_mode_config_reset(struct drm_device *dev) 3303235783Skib{ 3304235783Skib struct drm_crtc *crtc; 3305235783Skib struct drm_encoder *encoder; 3306235783Skib struct drm_connector *connector; 3307235783Skib 3308235783Skib list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) 3309235783Skib if (crtc->funcs->reset) 3310235783Skib crtc->funcs->reset(crtc); 3311235783Skib 3312235783Skib list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) 3313235783Skib if (encoder->funcs->reset) 3314235783Skib encoder->funcs->reset(encoder); 3315235783Skib 3316235783Skib list_for_each_entry(connector, &dev->mode_config.connector_list, head) 3317235783Skib if (connector->funcs->reset) 3318235783Skib connector->funcs->reset(connector); 3319235783Skib} 3320235783Skib 3321235783Skibint drm_mode_create_dumb_ioctl(struct drm_device *dev, 3322235783Skib void *data, struct drm_file *file_priv) 3323235783Skib{ 3324235783Skib struct drm_mode_create_dumb *args = data; 3325235783Skib 3326235783Skib if (!dev->driver->dumb_create) 3327235783Skib return -ENOTSUP; 3328235783Skib return dev->driver->dumb_create(file_priv, dev, args); 3329235783Skib} 3330235783Skib 3331235783Skibint drm_mode_mmap_dumb_ioctl(struct drm_device *dev, 3332235783Skib void *data, struct drm_file *file_priv) 3333235783Skib{ 3334235783Skib struct drm_mode_map_dumb *args = data; 3335235783Skib 3336235783Skib /* call driver ioctl to get mmap offset */ 3337235783Skib if (!dev->driver->dumb_map_offset) 3338235783Skib return -ENOTSUP; 3339235783Skib 3340235783Skib return dev->driver->dumb_map_offset(file_priv, dev, args->handle, &args->offset); 3341235783Skib} 3342235783Skib 3343235783Skibint drm_mode_destroy_dumb_ioctl(struct drm_device *dev, 3344235783Skib void *data, struct drm_file *file_priv) 3345235783Skib{ 3346235783Skib struct drm_mode_destroy_dumb *args = data; 3347235783Skib 3348235783Skib if (!dev->driver->dumb_destroy) 3349235783Skib return -ENOTSUP; 3350235783Skib 3351235783Skib return dev->driver->dumb_destroy(file_priv, dev, args->handle); 3352235783Skib} 3353235783Skib 3354235783Skib/* 3355235783Skib * Just need to support RGB formats here for compat with code that doesn't 3356235783Skib * use pixel formats directly yet. 3357235783Skib */ 3358235783Skibvoid drm_fb_get_bpp_depth(uint32_t format, unsigned int *depth, 3359235783Skib int *bpp) 3360235783Skib{ 3361235783Skib switch (format) { 3362235783Skib case DRM_FORMAT_RGB332: 3363235783Skib case DRM_FORMAT_BGR233: 3364235783Skib *depth = 8; 3365235783Skib *bpp = 8; 3366235783Skib break; 3367235783Skib case DRM_FORMAT_XRGB1555: 3368235783Skib case DRM_FORMAT_XBGR1555: 3369235783Skib case DRM_FORMAT_RGBX5551: 3370235783Skib case DRM_FORMAT_BGRX5551: 3371235783Skib case DRM_FORMAT_ARGB1555: 3372235783Skib case DRM_FORMAT_ABGR1555: 3373235783Skib case DRM_FORMAT_RGBA5551: 3374235783Skib case DRM_FORMAT_BGRA5551: 3375235783Skib *depth = 15; 3376235783Skib *bpp = 16; 3377235783Skib break; 3378235783Skib case DRM_FORMAT_RGB565: 3379235783Skib case DRM_FORMAT_BGR565: 3380235783Skib *depth = 16; 3381235783Skib *bpp = 16; 3382235783Skib break; 3383235783Skib case DRM_FORMAT_RGB888: 3384235783Skib case DRM_FORMAT_BGR888: 3385235783Skib *depth = 24; 3386235783Skib *bpp = 24; 3387235783Skib break; 3388235783Skib case DRM_FORMAT_XRGB8888: 3389235783Skib case DRM_FORMAT_XBGR8888: 3390235783Skib case DRM_FORMAT_RGBX8888: 3391235783Skib case DRM_FORMAT_BGRX8888: 3392235783Skib *depth = 24; 3393235783Skib *bpp = 32; 3394235783Skib break; 3395235783Skib case DRM_FORMAT_XRGB2101010: 3396235783Skib case DRM_FORMAT_XBGR2101010: 3397235783Skib case DRM_FORMAT_RGBX1010102: 3398235783Skib case DRM_FORMAT_BGRX1010102: 3399235783Skib case DRM_FORMAT_ARGB2101010: 3400235783Skib case DRM_FORMAT_ABGR2101010: 3401235783Skib case DRM_FORMAT_RGBA1010102: 3402235783Skib case DRM_FORMAT_BGRA1010102: 3403235783Skib *depth = 30; 3404235783Skib *bpp = 32; 3405235783Skib break; 3406235783Skib case DRM_FORMAT_ARGB8888: 3407235783Skib case DRM_FORMAT_ABGR8888: 3408235783Skib case DRM_FORMAT_RGBA8888: 3409235783Skib case DRM_FORMAT_BGRA8888: 3410235783Skib *depth = 32; 3411235783Skib *bpp = 32; 3412235783Skib break; 3413235783Skib default: 3414235783Skib DRM_DEBUG_KMS("unsupported pixel format\n"); 3415235783Skib *depth = 0; 3416235783Skib *bpp = 0; 3417235783Skib break; 3418235783Skib } 3419235783Skib} 3420