X-Git-Url: http://git.cascardo.info/?a=blobdiff_plain;f=drivers%2Fgpu%2Fdrm%2Fdrm_crtc.c;h=2985e3ff364f47c04edbf966102da9f32ad41d95;hb=0e2cfc005b376ed7b5c9a9fc466b5842fcc18cc7;hp=e79c8d3700d838ff87f1095bcb92a773dbe36018;hpb=911883759fed56c32b97d73154039b5f8ffcba93;p=cascardo%2Flinux.git diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index e79c8d3700d8..2985e3ff364f 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -683,7 +683,7 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc, drm_modeset_lock_init(&crtc->mutex); ret = drm_mode_object_get(dev, &crtc->base, DRM_MODE_OBJECT_CRTC); if (ret) - goto out; + return ret; crtc->base.properties = &crtc->properties; @@ -697,9 +697,7 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc, if (cursor) cursor->possible_crtcs = 1 << drm_crtc_index(crtc); - out: - - return ret; + return 0; } EXPORT_SYMBOL(drm_crtc_init_with_planes); @@ -723,6 +721,10 @@ void drm_crtc_cleanup(struct drm_crtc *crtc) drm_mode_object_put(dev, &crtc->base); list_del(&crtc->head); dev->mode_config.num_crtc--; + + WARN_ON(crtc->state && !crtc->funcs->atomic_destroy_state); + if (crtc->state && crtc->funcs->atomic_destroy_state) + crtc->funcs->atomic_destroy_state(crtc, crtc->state); } EXPORT_SYMBOL(drm_crtc_cleanup); @@ -766,7 +768,6 @@ static void drm_mode_remove(struct drm_connector *connector, /** * drm_connector_get_cmdline_mode - reads the user's cmdline mode * @connector: connector to quwery - * @mode: returned mode * * The kernel supports per-connector configration of its consoles through * use of the video= parameter. This function parses that option and @@ -870,6 +871,8 @@ int drm_connector_init(struct drm_device *dev, drm_connector_get_cmdline_mode(connector); + /* We should add connectors at the end to avoid upsetting the connector + * index too much. */ list_add_tail(&connector->head, &dev->mode_config.connector_list); dev->mode_config.num_connector++; @@ -919,6 +922,11 @@ void drm_connector_cleanup(struct drm_connector *connector) connector->name = NULL; list_del(&connector->head); dev->mode_config.num_connector--; + + WARN_ON(connector->state && !connector->funcs->atomic_destroy_state); + if (connector->state && connector->funcs->atomic_destroy_state) + connector->funcs->atomic_destroy_state(connector, + connector->state); } EXPORT_SYMBOL(drm_connector_cleanup); @@ -933,6 +941,9 @@ unsigned int drm_connector_index(struct drm_connector *connector) { unsigned int index = 0; struct drm_connector *tmp; + struct drm_mode_config *config = &connector->dev->mode_config; + + WARN_ON(!drm_modeset_is_locked(&config->connection_mutex)); list_for_each_entry(tmp, &connector->dev->mode_config.connector_list, head) { if (tmp == connector) @@ -1153,11 +1164,11 @@ int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane, { int ret; - drm_modeset_lock_all(dev); - ret = drm_mode_object_get(dev, &plane->base, DRM_MODE_OBJECT_PLANE); if (ret) - goto out; + return ret; + + drm_modeset_lock_init(&plane->mutex); plane->base.properties = &plane->properties; plane->dev = dev; @@ -1167,8 +1178,7 @@ int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane, if (!plane->format_types) { DRM_DEBUG_KMS("out of memory when allocating plane\n"); drm_mode_object_put(dev, &plane->base); - ret = -ENOMEM; - goto out; + return -ENOMEM; } memcpy(plane->format_types, formats, format_count * sizeof(uint32_t)); @@ -1185,10 +1195,7 @@ int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane, dev->mode_config.plane_type_property, plane->type); - out: - drm_modeset_unlock_all(dev); - - return ret; + return 0; } EXPORT_SYMBOL(drm_universal_plane_init); @@ -1246,6 +1253,10 @@ void drm_plane_cleanup(struct drm_plane *plane) if (plane->type == DRM_PLANE_TYPE_OVERLAY) dev->mode_config.num_overlay_plane--; drm_modeset_unlock_all(dev); + + WARN_ON(plane->state && !plane->funcs->atomic_destroy_state); + if (plane->state && plane->funcs->atomic_destroy_state) + plane->funcs->atomic_destroy_state(plane, plane->state); } EXPORT_SYMBOL(drm_plane_cleanup); @@ -1388,12 +1399,13 @@ EXPORT_SYMBOL(drm_mode_create_dvi_i_properties); * responsible for allocating a list of format names and passing them to * this routine. */ -int drm_mode_create_tv_properties(struct drm_device *dev, int num_modes, +int drm_mode_create_tv_properties(struct drm_device *dev, + unsigned int num_modes, char *modes[]) { struct drm_property *tv_selector; struct drm_property *tv_subconnector; - int i; + unsigned int i; if (dev->mode_config.tv_select_subconnector_property) return 0; @@ -1491,7 +1503,7 @@ EXPORT_SYMBOL(drm_mode_create_scaling_mode_property); * connectors. * * Returns: - * Zero on success, errno on failure. + * Zero on success, negative errno on failure. */ int drm_mode_create_aspect_ratio_property(struct drm_device *dev) { @@ -1535,6 +1547,30 @@ int drm_mode_create_dirty_info_property(struct drm_device *dev) } EXPORT_SYMBOL(drm_mode_create_dirty_info_property); +/** + * drm_mode_create_suggested_offset_properties - create suggests offset properties + * @dev: DRM device + * + * Create the the suggested x/y offset property for connectors. + */ +int drm_mode_create_suggested_offset_properties(struct drm_device *dev) +{ + if (dev->mode_config.suggested_x_property && dev->mode_config.suggested_y_property) + return 0; + + dev->mode_config.suggested_x_property = + drm_property_create_range(dev, DRM_MODE_PROP_IMMUTABLE, "suggested X", 0, 0xffffffff); + + dev->mode_config.suggested_y_property = + drm_property_create_range(dev, DRM_MODE_PROP_IMMUTABLE, "suggested Y", 0, 0xffffffff); + + if (dev->mode_config.suggested_x_property == NULL || + dev->mode_config.suggested_y_property == NULL) + return -ENOMEM; + return 0; +} +EXPORT_SYMBOL(drm_mode_create_suggested_offset_properties); + static int drm_mode_group_init(struct drm_device *dev, struct drm_mode_group *group) { uint32_t total_objects = 0; @@ -1651,7 +1687,7 @@ static void drm_crtc_convert_to_umode(struct drm_mode_modeinfo *out, * the caller. * * Returns: - * Zero on success, errno on failure. + * Zero on success, negative errno on failure. */ static int drm_crtc_convert_umode(struct drm_display_mode *out, const struct drm_mode_modeinfo *in) @@ -1694,7 +1730,7 @@ static int drm_crtc_convert_umode(struct drm_display_mode *out, * Called by the user via ioctl. * * Returns: - * Zero on success, errno on failure. + * Zero on success, negative errno on failure. */ int drm_mode_getresources(struct drm_device *dev, void *data, struct drm_file *file_priv) @@ -1745,7 +1781,9 @@ int drm_mode_getresources(struct drm_device *dev, void *data, card_res->count_fbs = fb_count; mutex_unlock(&file_priv->fbs_lock); - drm_modeset_lock_all(dev); + /* mode_config.mutex protects the connector list against e.g. DP MST + * connector hot-adding. CRTC/Plane lists are invariant. */ + mutex_lock(&dev->mode_config.mutex); if (!drm_is_primary_client(file_priv)) { mode_group = NULL; @@ -1865,7 +1903,7 @@ int drm_mode_getresources(struct drm_device *dev, void *data, card_res->count_connectors, card_res->count_encoders); out: - drm_modeset_unlock_all(dev); + mutex_unlock(&dev->mode_config.mutex); return ret; } @@ -1880,26 +1918,22 @@ out: * Called by the user via ioctl. * * Returns: - * Zero on success, errno on failure. + * Zero on success, negative errno on failure. */ int drm_mode_getcrtc(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_crtc *crtc_resp = data; struct drm_crtc *crtc; - int ret = 0; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - drm_modeset_lock_all(dev); - crtc = drm_crtc_find(dev, crtc_resp->crtc_id); - if (!crtc) { - ret = -ENOENT; - goto out; - } + if (!crtc) + return -ENOENT; + drm_modeset_lock_crtc(crtc, crtc->primary); crtc_resp->x = crtc->x; crtc_resp->y = crtc->y; crtc_resp->gamma_size = crtc->gamma_size; @@ -1916,10 +1950,9 @@ int drm_mode_getcrtc(struct drm_device *dev, } else { crtc_resp->mode_valid = 0; } + drm_modeset_unlock_crtc(crtc); -out: - drm_modeset_unlock_all(dev); - return ret; + return 0; } static bool drm_mode_expose_to_userspace(const struct drm_display_mode *mode, @@ -1935,6 +1968,15 @@ static bool drm_mode_expose_to_userspace(const struct drm_display_mode *mode, return true; } +static struct drm_encoder *drm_connector_get_encoder(struct drm_connector *connector) +{ + /* For atomic drivers only state objects are synchronously updated and + * protected by modeset locks, so check those first. */ + if (connector->state) + return connector->state->best_encoder; + return connector->encoder; +} + /** * drm_mode_getconnector - get connector configuration * @dev: drm device for the ioctl @@ -1946,13 +1988,14 @@ static bool drm_mode_expose_to_userspace(const struct drm_display_mode *mode, * Called by the user via ioctl. * * Returns: - * Zero on success, errno on failure. + * Zero on success, negative errno on failure. */ int drm_mode_getconnector(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_get_connector *out_resp = data; struct drm_connector *connector; + struct drm_encoder *encoder; struct drm_display_mode *mode; int mode_count = 0; int props_count = 0; @@ -2008,8 +2051,10 @@ int drm_mode_getconnector(struct drm_device *dev, void *data, out_resp->subpixel = connector->display_info.subpixel_order; out_resp->connection = connector->status; drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); - if (connector->encoder) - out_resp->encoder_id = connector->encoder->base.id; + + encoder = drm_connector_get_encoder(connector); + if (encoder) + out_resp->encoder_id = encoder->base.id; else out_resp->encoder_id = 0; drm_modeset_unlock(&dev->mode_config.connection_mutex); @@ -2079,6 +2124,33 @@ out: return ret; } +static struct drm_crtc *drm_encoder_get_crtc(struct drm_encoder *encoder) +{ + struct drm_connector *connector; + struct drm_device *dev = encoder->dev; + bool uses_atomic = false; + + /* For atomic drivers only state objects are synchronously updated and + * protected by modeset locks, so check those first. */ + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + if (!connector->state) + continue; + + uses_atomic = true; + + if (connector->state->best_encoder != encoder) + continue; + + return connector->state->crtc; + } + + /* Don't return stale data (e.g. pending async disable). */ + if (uses_atomic) + return NULL; + + return encoder->crtc; +} + /** * drm_mode_getencoder - get encoder configuration * @dev: drm device for the ioctl @@ -2090,37 +2162,38 @@ out: * Called by the user via ioctl. * * Returns: - * Zero on success, errno on failure. + * Zero on success, negative errno on failure. */ int drm_mode_getencoder(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_get_encoder *enc_resp = data; struct drm_encoder *encoder; - int ret = 0; + struct drm_crtc *crtc; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - drm_modeset_lock_all(dev); encoder = drm_encoder_find(dev, enc_resp->encoder_id); - if (!encoder) { - ret = -ENOENT; - goto out; - } + if (!encoder) + return -ENOENT; - if (encoder->crtc) + drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); + crtc = drm_encoder_get_crtc(encoder); + if (crtc) + enc_resp->crtc_id = crtc->base.id; + else if (encoder->crtc) enc_resp->crtc_id = encoder->crtc->base.id; else enc_resp->crtc_id = 0; + drm_modeset_unlock(&dev->mode_config.connection_mutex); + enc_resp->encoder_type = encoder->encoder_type; enc_resp->encoder_id = encoder->base.id; enc_resp->possible_crtcs = encoder->possible_crtcs; enc_resp->possible_clones = encoder->possible_clones; -out: - drm_modeset_unlock_all(dev); - return ret; + return 0; } /** @@ -2134,7 +2207,7 @@ out: * Called by the user via ioctl. * * Returns: - * Zero on success, errno on failure. + * Zero on success, negative errno on failure. */ int drm_mode_getplane_res(struct drm_device *dev, void *data, struct drm_file *file_priv) @@ -2143,13 +2216,12 @@ int drm_mode_getplane_res(struct drm_device *dev, void *data, struct drm_mode_config *config; struct drm_plane *plane; uint32_t __user *plane_ptr; - int copied = 0, ret = 0; + int copied = 0; unsigned num_planes; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - drm_modeset_lock_all(dev); config = &dev->mode_config; if (file_priv->universal_planes) @@ -2165,6 +2237,7 @@ int drm_mode_getplane_res(struct drm_device *dev, void *data, (plane_resp->count_planes >= num_planes)) { plane_ptr = (uint32_t __user *)(unsigned long)plane_resp->plane_id_ptr; + /* Plane lists are invariant, no locking needed. */ list_for_each_entry(plane, &config->plane_list, head) { /* * Unless userspace set the 'universal planes' @@ -2174,18 +2247,14 @@ int drm_mode_getplane_res(struct drm_device *dev, void *data, !file_priv->universal_planes) continue; - if (put_user(plane->base.id, plane_ptr + copied)) { - ret = -EFAULT; - goto out; - } + if (put_user(plane->base.id, plane_ptr + copied)) + return -EFAULT; copied++; } } plane_resp->count_planes = num_planes; -out: - drm_modeset_unlock_all(dev); - return ret; + return 0; } /** @@ -2199,7 +2268,7 @@ out: * Called by the user via ioctl. * * Returns: - * Zero on success, errno on failure. + * Zero on success, negative errno on failure. */ int drm_mode_getplane(struct drm_device *dev, void *data, struct drm_file *file_priv) @@ -2207,18 +2276,15 @@ int drm_mode_getplane(struct drm_device *dev, void *data, struct drm_mode_get_plane *plane_resp = data; struct drm_plane *plane; uint32_t __user *format_ptr; - int ret = 0; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - drm_modeset_lock_all(dev); plane = drm_plane_find(dev, plane_resp->plane_id); - if (!plane) { - ret = -ENOENT; - goto out; - } + if (!plane) + return -ENOENT; + drm_modeset_lock(&plane->mutex, NULL); if (plane->crtc) plane_resp->crtc_id = plane->crtc->base.id; else @@ -2228,6 +2294,7 @@ int drm_mode_getplane(struct drm_device *dev, void *data, plane_resp->fb_id = plane->fb->base.id; else plane_resp->fb_id = 0; + drm_modeset_unlock(&plane->mutex); plane_resp->plane_id = plane->base.id; plane_resp->possible_crtcs = plane->possible_crtcs; @@ -2243,15 +2310,12 @@ int drm_mode_getplane(struct drm_device *dev, void *data, if (copy_to_user(format_ptr, plane->format_types, sizeof(uint32_t) * plane->format_count)) { - ret = -EFAULT; - goto out; + return -EFAULT; } } plane_resp->count_format_types = plane->format_count; -out: - drm_modeset_unlock_all(dev); - return ret; + return 0; } /* @@ -2274,7 +2338,7 @@ static int __setplane_internal(struct drm_plane *plane, { int ret = 0; unsigned int fb_width, fb_height; - int i; + unsigned int i; /* No fb means shut it down */ if (!fb) { @@ -2378,13 +2442,12 @@ static int setplane_internal(struct drm_plane *plane, * valid crtc). * * Returns: - * Zero on success, errno on failure. + * Zero on success, negative errno on failure. */ int drm_mode_setplane(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_set_plane *plane_req = data; - struct drm_mode_object *obj; struct drm_plane *plane; struct drm_crtc *crtc = NULL; struct drm_framebuffer *fb = NULL; @@ -2407,14 +2470,12 @@ int drm_mode_setplane(struct drm_device *dev, void *data, * First, find the plane, crtc, and fb objects. If not available, * we don't bother to call the driver. */ - obj = drm_mode_object_find(dev, plane_req->plane_id, - DRM_MODE_OBJECT_PLANE); - if (!obj) { + plane = drm_plane_find(dev, plane_req->plane_id); + if (!plane) { DRM_DEBUG_KMS("Unknown plane ID %d\n", plane_req->plane_id); return -ENOENT; } - plane = obj_to_plane(obj); if (plane_req->fb_id) { fb = drm_framebuffer_lookup(dev, plane_req->fb_id); @@ -2424,14 +2485,12 @@ int drm_mode_setplane(struct drm_device *dev, void *data, return -ENOENT; } - obj = drm_mode_object_find(dev, plane_req->crtc_id, - DRM_MODE_OBJECT_CRTC); - if (!obj) { + crtc = drm_crtc_find(dev, plane_req->crtc_id); + if (!crtc) { DRM_DEBUG_KMS("Unknown crtc ID %d\n", plane_req->crtc_id); return -ENOENT; } - crtc = obj_to_crtc(obj); } /* @@ -2453,7 +2512,7 @@ int drm_mode_setplane(struct drm_device *dev, void *data, * interface. The only thing it adds is correct refcounting dance. * * Returns: - * Zero on success, errno on failure. + * Zero on success, negative errno on failure. */ int drm_mode_set_config_internal(struct drm_mode_set *set) { @@ -2490,6 +2549,27 @@ int drm_mode_set_config_internal(struct drm_mode_set *set) } EXPORT_SYMBOL(drm_mode_set_config_internal); +/** + * drm_crtc_get_hv_timing - Fetches hdisplay/vdisplay for given mode + * @mode: mode to query + * @hdisplay: hdisplay value to fill in + * @vdisplay: vdisplay value to fill in + * + * The vdisplay value will be doubled if the specified mode is a stereo mode of + * the appropriate layout. + */ +void drm_crtc_get_hv_timing(const struct drm_display_mode *mode, + int *hdisplay, int *vdisplay) +{ + struct drm_display_mode adjusted; + + drm_mode_copy(&adjusted, mode); + drm_mode_set_crtcinfo(&adjusted, CRTC_STEREO_DOUBLE_ONLY); + *hdisplay = adjusted.crtc_hdisplay; + *vdisplay = adjusted.crtc_vdisplay; +} +EXPORT_SYMBOL(drm_crtc_get_hv_timing); + /** * drm_crtc_check_viewport - Checks that a framebuffer is big enough for the * CRTC viewport @@ -2507,16 +2587,7 @@ int drm_crtc_check_viewport(const struct drm_crtc *crtc, { int hdisplay, vdisplay; - hdisplay = mode->hdisplay; - vdisplay = mode->vdisplay; - - if (drm_mode_is_stereo(mode)) { - struct drm_display_mode adjusted = *mode; - - drm_mode_set_crtcinfo(&adjusted, CRTC_STEREO_DOUBLE); - hdisplay = adjusted.crtc_hdisplay; - vdisplay = adjusted.crtc_vdisplay; - } + drm_crtc_get_hv_timing(mode, &hdisplay, &vdisplay); if (crtc->invert_dimensions) swap(hdisplay, vdisplay); @@ -2546,7 +2617,7 @@ EXPORT_SYMBOL(drm_crtc_check_viewport); * Called by the user via ioctl. * * Returns: - * Zero on success, errno on failure. + * Zero on success, negative errno on failure. */ int drm_mode_setcrtc(struct drm_device *dev, void *data, struct drm_file *file_priv) @@ -2709,7 +2780,7 @@ out: * userspace wants to make use of these capabilities. * * Returns: - * Zero on success, errno on failure. + * Zero on success, negative errno on failure. */ static int drm_mode_cursor_universal(struct drm_crtc *crtc, struct drm_mode_cursor2 *req, @@ -2810,7 +2881,7 @@ static int drm_mode_cursor_common(struct drm_device *dev, * If this crtc has a universal cursor plane, call that plane's update * handler rather than using legacy cursor handlers. */ - drm_modeset_lock_crtc(crtc); + drm_modeset_lock_crtc(crtc, crtc->cursor); if (crtc->cursor) { ret = drm_mode_cursor_universal(crtc, req, file_priv); goto out; @@ -2857,7 +2928,7 @@ out: * Called by the user via ioctl. * * Returns: - * Zero on success, errno on failure. + * Zero on success, negative errno on failure. */ int drm_mode_cursor_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) @@ -2884,7 +2955,7 @@ int drm_mode_cursor_ioctl(struct drm_device *dev, * Called by the user via ioctl. * * Returns: - * Zero on success, errno on failure. + * Zero on success, negative errno on failure. */ int drm_mode_cursor2_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) @@ -2943,23 +3014,21 @@ EXPORT_SYMBOL(drm_mode_legacy_fb_format); * @file_priv: drm file for the ioctl call * * Add a new FB to the specified CRTC, given a user request. This is the - * original addfb ioclt which only supported RGB formats. + * original addfb ioctl which only supported RGB formats. * * Called by the user via ioctl. * * Returns: - * Zero on success, errno on failure. + * Zero on success, negative errno on failure. */ int drm_mode_addfb(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_fb_cmd *or = data; struct drm_mode_fb_cmd2 r = {}; - struct drm_mode_config *config = &dev->mode_config; - struct drm_framebuffer *fb; - int ret = 0; + int ret; - /* Use new struct with format internally */ + /* convert to new format and call new ioctl */ r.fb_id = or->fb_id; r.width = or->width; r.height = or->height; @@ -2967,28 +3036,13 @@ int drm_mode_addfb(struct drm_device *dev, r.pixel_format = drm_mode_legacy_fb_format(or->bpp, or->depth); r.handles[0] = or->handle; - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return -EINVAL; - - if ((config->min_width > r.width) || (r.width > config->max_width)) - return -EINVAL; - - if ((config->min_height > r.height) || (r.height > config->max_height)) - return -EINVAL; - - fb = dev->mode_config.funcs->fb_create(dev, file_priv, &r); - if (IS_ERR(fb)) { - DRM_DEBUG_KMS("could not create framebuffer\n"); - return PTR_ERR(fb); - } + ret = drm_mode_addfb2(dev, &r, file_priv); + if (ret) + return ret; - mutex_lock(&file_priv->fbs_lock); - or->fb_id = fb->base.id; - list_add(&fb->filp_head, &file_priv->fbs); - DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id); - mutex_unlock(&file_priv->fbs_lock); + or->fb_id = r.fb_id; - return ret; + return 0; } static int format_check(const struct drm_mode_fb_cmd2 *r) @@ -3080,7 +3134,7 @@ static int framebuffer_check(const struct drm_mode_fb_cmd2 *r) num_planes = drm_format_num_planes(r->pixel_format); if (r->width == 0 || r->width % hsub) { - DRM_DEBUG_KMS("bad framebuffer width %u\n", r->height); + DRM_DEBUG_KMS("bad framebuffer width %u\n", r->width); return -EINVAL; } @@ -3170,7 +3224,7 @@ static struct drm_framebuffer *add_framebuffer_internal(struct drm_device *dev, * Called by the user via ioctl. * * Returns: - * Zero on success, errno on failure. + * Zero on success, negative errno on failure. */ int drm_mode_addfb2(struct drm_device *dev, void *data, struct drm_file *file_priv) @@ -3198,7 +3252,7 @@ int drm_mode_addfb2(struct drm_device *dev, * Called by the user via ioctl. * * Returns: - * Zero on success, errno on failure. + * Zero on success, negative errno on failure. */ int drm_mode_rmfb(struct drm_device *dev, void *data, struct drm_file *file_priv) @@ -3252,7 +3306,7 @@ fail_lookup: * Called by the user via ioctl. * * Returns: - * Zero on success, errno on failure. + * Zero on success, negative errno on failure. */ int drm_mode_getfb(struct drm_device *dev, void *data, struct drm_file *file_priv) @@ -3313,7 +3367,7 @@ int drm_mode_getfb(struct drm_device *dev, * Called by the user via ioctl. * * Returns: - * Zero on success, errno on failure. + * Zero on success, negative errno on failure. */ int drm_mode_dirtyfb_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) @@ -3393,7 +3447,7 @@ out_err1: * Called by the user via ioctl. * * Returns: - * Zero on success, errno on failure. + * Zero on success, negative errno on failure. */ void drm_fb_release(struct drm_file *priv) { @@ -3435,6 +3489,10 @@ void drm_fb_release(struct drm_file *priv) * object with drm_object_attach_property. The returned property object must be * freed with drm_property_destroy. * + * Note that the DRM core keeps a per-device list of properties and that, if + * drm_mode_config_cleanup() is called, it will destroy all properties created + * by the driver. + * * Returns: * A pointer to the newly created property on success, NULL on failure. */ @@ -3462,7 +3520,7 @@ struct drm_property *drm_property_create(struct drm_device *dev, int flags, property->flags = flags; property->num_values = num_values; - INIT_LIST_HEAD(&property->enum_blob_list); + INIT_LIST_HEAD(&property->enum_list); if (name) { strncpy(property->name, name, DRM_PROP_NAME_LEN); @@ -3611,7 +3669,7 @@ static struct drm_property *property_create_range(struct drm_device *dev, * object with drm_object_attach_property. The returned property object must be * freed with drm_property_destroy. * - * Userspace is allowed to set any interger value in the (min, max) range + * Userspace is allowed to set any integer value in the (min, max) range * inclusive. * * Returns: @@ -3684,8 +3742,8 @@ int drm_property_add_enum(struct drm_property *property, int index, (value > 63)) return -EINVAL; - if (!list_empty(&property->enum_blob_list)) { - list_for_each_entry(prop_enum, &property->enum_blob_list, head) { + if (!list_empty(&property->enum_list)) { + list_for_each_entry(prop_enum, &property->enum_list, head) { if (prop_enum->value == value) { strncpy(prop_enum->name, name, DRM_PROP_NAME_LEN); prop_enum->name[DRM_PROP_NAME_LEN-1] = '\0'; @@ -3703,7 +3761,7 @@ int drm_property_add_enum(struct drm_property *property, int index, prop_enum->value = value; property->values[index] = value; - list_add_tail(&prop_enum->head, &property->enum_blob_list); + list_add_tail(&prop_enum->head, &property->enum_list); return 0; } EXPORT_SYMBOL(drm_property_add_enum); @@ -3720,7 +3778,7 @@ void drm_property_destroy(struct drm_device *dev, struct drm_property *property) { struct drm_property_enum *prop_enum, *pt; - list_for_each_entry_safe(prop_enum, pt, &property->enum_blob_list, head) { + list_for_each_entry_safe(prop_enum, pt, &property->enum_list, head) { list_del(&prop_enum->head); kfree(prop_enum); } @@ -3823,17 +3881,20 @@ int drm_object_property_get_value(struct drm_mode_object *obj, EXPORT_SYMBOL(drm_object_property_get_value); /** - * drm_mode_getproperty_ioctl - get the current value of a connector's property + * drm_mode_getproperty_ioctl - get the property metadata * @dev: DRM device * @data: ioctl data * @file_priv: DRM file info * - * This function retrieves the current value for an connectors's property. + * This function retrieves the metadata for a given property, like the different + * possible values for an enum property or the limits for a range property. + * + * Blob properties are special * * Called by the user via ioctl. * * Returns: - * Zero on success, errno on failure. + * Zero on success, negative errno on failure. */ int drm_mode_getproperty_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) @@ -3841,16 +3902,12 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev, struct drm_mode_get_property *out_resp = data; struct drm_property *property; int enum_count = 0; - int blob_count = 0; int value_count = 0; int ret = 0, i; int copied; struct drm_property_enum *prop_enum; struct drm_mode_property_enum __user *enum_ptr; - struct drm_property_blob *prop_blob; - uint32_t __user *blob_id_ptr; uint64_t __user *values_ptr; - uint32_t __user *blob_length_ptr; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; @@ -3864,11 +3921,8 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev, if (drm_property_type_is(property, DRM_MODE_PROP_ENUM) || drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) { - list_for_each_entry(prop_enum, &property->enum_blob_list, head) + list_for_each_entry(prop_enum, &property->enum_list, head) enum_count++; - } else if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) { - list_for_each_entry(prop_blob, &property->enum_blob_list, head) - blob_count++; } value_count = property->num_values; @@ -3893,7 +3947,7 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev, if ((out_resp->count_enum_blobs >= enum_count) && enum_count) { copied = 0; enum_ptr = (struct drm_mode_property_enum __user *)(unsigned long)out_resp->enum_blob_ptr; - list_for_each_entry(prop_enum, &property->enum_blob_list, head) { + list_for_each_entry(prop_enum, &property->enum_list, head) { if (copy_to_user(&enum_ptr[copied].value, &prop_enum->value, sizeof(uint64_t))) { ret = -EFAULT; @@ -3911,35 +3965,24 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev, out_resp->count_enum_blobs = enum_count; } - if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) { - if ((out_resp->count_enum_blobs >= blob_count) && blob_count) { - copied = 0; - blob_id_ptr = (uint32_t __user *)(unsigned long)out_resp->enum_blob_ptr; - blob_length_ptr = (uint32_t __user *)(unsigned long)out_resp->values_ptr; - - list_for_each_entry(prop_blob, &property->enum_blob_list, head) { - if (put_user(prop_blob->base.id, blob_id_ptr + copied)) { - ret = -EFAULT; - goto done; - } - - if (put_user(prop_blob->length, blob_length_ptr + copied)) { - ret = -EFAULT; - goto done; - } - - copied++; - } - } - out_resp->count_enum_blobs = blob_count; - } + /* + * NOTE: The idea seems to have been to use this to read all the blob + * property values. But nothing ever added them to the corresponding + * list, userspace always used the special-purpose get_blob ioctl to + * read the value for a blob property. It also doesn't make a lot of + * sense to return values here when everything else is just metadata for + * the property itself. + */ + if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) + out_resp->count_enum_blobs = 0; done: drm_modeset_unlock_all(dev); return ret; } -static struct drm_property_blob *drm_property_create_blob(struct drm_device *dev, int length, - void *data) +static struct drm_property_blob * +drm_property_create_blob(struct drm_device *dev, size_t length, + const void *data) { struct drm_property_blob *blob; int ret; @@ -3985,7 +4028,7 @@ static void drm_property_destroy_blob(struct drm_device *dev, * Called by the user via ioctl. * * Returns: - * Zero on success, errno on failure. + * Zero on success, negative errno on failure. */ int drm_mode_getblob_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) @@ -4019,12 +4062,25 @@ done: return ret; } +/** + * drm_mode_connector_set_path_property - set tile property on connector + * @connector: connector to set property on. + * @path: path to use for property. + * + * This creates a property to expose to userspace to specify a + * connector path. This is mainly used for DisplayPort MST where + * connectors have a topology and we want to allow userspace to give + * them more meaningful names. + * + * Returns: + * Zero on success, negative errno on failure. + */ int drm_mode_connector_set_path_property(struct drm_connector *connector, - char *path) + const char *path) { struct drm_device *dev = connector->dev; - int ret, size; - size = strlen(path) + 1; + size_t size = strlen(path) + 1; + int ret; connector->path_blob_ptr = drm_property_create_blob(connector->dev, size, path); @@ -4047,13 +4103,14 @@ EXPORT_SYMBOL(drm_mode_connector_set_path_property); * connector's edid property. * * Returns: - * Zero on success, errno on failure. + * Zero on success, negative errno on failure. */ int drm_mode_connector_update_edid_property(struct drm_connector *connector, - struct edid *edid) + const struct edid *edid) { struct drm_device *dev = connector->dev; - int ret, size; + size_t size; + int ret; /* ignore requests to set edid when overridden */ if (connector->override_edid) @@ -4143,7 +4200,7 @@ static bool drm_property_change_is_valid(struct drm_property *property, * Called by the user via ioctl. * * Returns: - * Zero on success, errno on failure. + * Zero on success, negative errno on failure. */ int drm_mode_connector_property_set_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) @@ -4226,7 +4283,7 @@ int drm_mode_plane_set_obj_prop(struct drm_plane *plane, EXPORT_SYMBOL(drm_mode_plane_set_obj_prop); /** - * drm_mode_getproperty_ioctl - get the current value of a object's property + * drm_mode_obj_get_properties_ioctl - get the current value of a object's property * @dev: DRM device * @data: ioctl data * @file_priv: DRM file info @@ -4238,7 +4295,7 @@ EXPORT_SYMBOL(drm_mode_plane_set_obj_prop); * Called by the user via ioctl. * * Returns: - * Zero on success, errno on failure. + * Zero on success, negative errno on failure. */ int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) @@ -4310,7 +4367,7 @@ out: * Called by the user via ioctl. * * Returns: - * Zero on success, errno on failure. + * Zero on success, negative errno on failure. */ int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) @@ -4382,7 +4439,7 @@ out: * possible_clones and possible_crtcs bitmasks. * * Returns: - * Zero on success, errno on failure. + * Zero on success, negative errno on failure. */ int drm_mode_connector_attach_encoder(struct drm_connector *connector, struct drm_encoder *encoder) @@ -4409,7 +4466,7 @@ EXPORT_SYMBOL(drm_mode_connector_attach_encoder); * fixed gamma table size. * * Returns: - * Zero on success, errno on failure. + * Zero on success, negative errno on failure. */ int drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc, int gamma_size) @@ -4438,7 +4495,7 @@ EXPORT_SYMBOL(drm_mode_crtc_set_gamma_size); * Called by the user via ioctl. * * Returns: - * Zero on success, errno on failure. + * Zero on success, negative errno on failure. */ int drm_mode_gamma_set_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) @@ -4510,7 +4567,7 @@ out: * Called by the user via ioctl. * * Returns: - * Zero on success, errno on failure. + * Zero on success, negative errno on failure. */ int drm_mode_gamma_get_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) @@ -4576,7 +4633,7 @@ out: * Called by the user via ioctl. * * Returns: - * Zero on success, errno on failure. + * Zero on success, negative errno on failure. */ int drm_mode_page_flip_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) @@ -4599,7 +4656,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, if (!crtc) return -ENOENT; - drm_modeset_lock_crtc(crtc); + drm_modeset_lock_crtc(crtc, crtc->primary); if (crtc->primary->fb == NULL) { /* The framebuffer is currently unbound, presumably * due to a hotplug event, that userspace has not @@ -4742,7 +4799,7 @@ EXPORT_SYMBOL(drm_mode_config_reset); * Called by the user via ioctl. * * Returns: - * Zero on success, errno on failure. + * Zero on success, negative errno on failure. */ int drm_mode_create_dumb_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) @@ -4769,6 +4826,16 @@ int drm_mode_create_dumb_ioctl(struct drm_device *dev, if (PAGE_ALIGN(size) == 0) return -EINVAL; + /* + * handle, pitch and size are output parameters. Zero them out to + * prevent drivers from accidentally using uninitialized data. Since + * not all existing userspace is clearing these fields properly we + * cannot reject IOCTL with garbage in them. + */ + args->handle = 0; + args->pitch = 0; + args->size = 0; + return dev->driver->dumb_create(file_priv, dev, args); } @@ -4784,7 +4851,7 @@ int drm_mode_create_dumb_ioctl(struct drm_device *dev, * Called by the user via ioctl. * * Returns: - * Zero on success, errno on failure. + * Zero on success, negative errno on failure. */ int drm_mode_mmap_dumb_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) @@ -4811,7 +4878,7 @@ int drm_mode_mmap_dumb_ioctl(struct drm_device *dev, * Called by the user via ioctl. * * Returns: - * Zero on success, errno on failure. + * Zero on success, negative errno on failure. */ int drm_mode_destroy_dumb_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv)