X-Git-Url: http://git.cascardo.info/?p=cascardo%2Flinux.git;a=blobdiff_plain;f=drivers%2Fgpu%2Fdrm%2Fdrm_crtc.c;h=7c1786df6213b0816ec444780c1947b52329f3a8;hp=2985e3ff364f47c04edbf966102da9f32ad41d95;hb=adc31849b27fefeca6c225d3895143a2ec6970fa;hpb=0e2cfc005b376ed7b5c9a9fc466b5842fcc18cc7 diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 2985e3ff364f..7c1786df6213 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -38,6 +38,7 @@ #include #include #include +#include #include "drm_crtc_internal.h" #include "drm_internal.h" @@ -61,8 +62,8 @@ static struct drm_framebuffer *add_framebuffer_internal(struct drm_device *dev, /* * Global properties */ -static const struct drm_prop_enum_list drm_dpms_enum_list[] = -{ { DRM_MODE_DPMS_ON, "On" }, +static const struct drm_prop_enum_list drm_dpms_enum_list[] = { + { DRM_MODE_DPMS_ON, "On" }, { DRM_MODE_DPMS_STANDBY, "Standby" }, { DRM_MODE_DPMS_SUSPEND, "Suspend" }, { DRM_MODE_DPMS_OFF, "Off" } @@ -70,8 +71,7 @@ static const struct drm_prop_enum_list drm_dpms_enum_list[] = DRM_ENUM_NAME_FN(drm_get_dpms_name, drm_dpms_enum_list) -static const struct drm_prop_enum_list drm_plane_type_enum_list[] = -{ +static const struct drm_prop_enum_list drm_plane_type_enum_list[] = { { DRM_PLANE_TYPE_OVERLAY, "Overlay" }, { DRM_PLANE_TYPE_PRIMARY, "Primary" }, { DRM_PLANE_TYPE_CURSOR, "Cursor" }, @@ -80,8 +80,7 @@ static const struct drm_prop_enum_list drm_plane_type_enum_list[] = /* * Optional properties */ -static const struct drm_prop_enum_list drm_scaling_mode_enum_list[] = -{ +static const struct drm_prop_enum_list drm_scaling_mode_enum_list[] = { { DRM_MODE_SCALE_NONE, "None" }, { DRM_MODE_SCALE_FULLSCREEN, "Full" }, { DRM_MODE_SCALE_CENTER, "Center" }, @@ -97,8 +96,7 @@ static const struct drm_prop_enum_list drm_aspect_ratio_enum_list[] = { /* * Non-global properties, but "required" for certain connectors. */ -static const struct drm_prop_enum_list drm_dvi_i_select_enum_list[] = -{ +static const struct drm_prop_enum_list drm_dvi_i_select_enum_list[] = { { DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */ { DRM_MODE_SUBCONNECTOR_DVID, "DVI-D" }, /* DVI-I */ { DRM_MODE_SUBCONNECTOR_DVIA, "DVI-A" }, /* DVI-I */ @@ -106,8 +104,7 @@ static const struct drm_prop_enum_list drm_dvi_i_select_enum_list[] = DRM_ENUM_NAME_FN(drm_get_dvi_i_select_name, drm_dvi_i_select_enum_list) -static const struct drm_prop_enum_list drm_dvi_i_subconnector_enum_list[] = -{ +static const struct drm_prop_enum_list drm_dvi_i_subconnector_enum_list[] = { { DRM_MODE_SUBCONNECTOR_Unknown, "Unknown" }, /* DVI-I and TV-out */ { DRM_MODE_SUBCONNECTOR_DVID, "DVI-D" }, /* DVI-I */ { DRM_MODE_SUBCONNECTOR_DVIA, "DVI-A" }, /* DVI-I */ @@ -116,8 +113,7 @@ static const struct drm_prop_enum_list drm_dvi_i_subconnector_enum_list[] = DRM_ENUM_NAME_FN(drm_get_dvi_i_subconnector_name, drm_dvi_i_subconnector_enum_list) -static const struct drm_prop_enum_list drm_tv_select_enum_list[] = -{ +static const struct drm_prop_enum_list drm_tv_select_enum_list[] = { { DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */ { DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */ { DRM_MODE_SUBCONNECTOR_SVIDEO, "SVIDEO" }, /* TV-out */ @@ -127,8 +123,7 @@ static const struct drm_prop_enum_list drm_tv_select_enum_list[] = DRM_ENUM_NAME_FN(drm_get_tv_select_name, drm_tv_select_enum_list) -static const struct drm_prop_enum_list drm_tv_subconnector_enum_list[] = -{ +static const struct drm_prop_enum_list drm_tv_subconnector_enum_list[] = { { DRM_MODE_SUBCONNECTOR_Unknown, "Unknown" }, /* DVI-I and TV-out */ { DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */ { DRM_MODE_SUBCONNECTOR_SVIDEO, "SVIDEO" }, /* TV-out */ @@ -154,8 +149,8 @@ struct drm_conn_prop_enum_list { /* * Connector and encoder types. */ -static struct drm_conn_prop_enum_list drm_connector_enum_list[] = -{ { DRM_MODE_CONNECTOR_Unknown, "Unknown" }, +static struct drm_conn_prop_enum_list drm_connector_enum_list[] = { + { DRM_MODE_CONNECTOR_Unknown, "Unknown" }, { DRM_MODE_CONNECTOR_VGA, "VGA" }, { DRM_MODE_CONNECTOR_DVII, "DVI-I" }, { DRM_MODE_CONNECTOR_DVID, "DVI-D" }, @@ -174,8 +169,8 @@ static struct drm_conn_prop_enum_list drm_connector_enum_list[] = { DRM_MODE_CONNECTOR_DSI, "DSI" }, }; -static const struct drm_prop_enum_list drm_encoder_enum_list[] = -{ { DRM_MODE_ENCODER_NONE, "None" }, +static const struct drm_prop_enum_list drm_encoder_enum_list[] = { + { DRM_MODE_ENCODER_NONE, "None" }, { DRM_MODE_ENCODER_DAC, "DAC" }, { DRM_MODE_ENCODER_TMDS, "TMDS" }, { DRM_MODE_ENCODER_LVDS, "LVDS" }, @@ -185,8 +180,7 @@ static const struct drm_prop_enum_list drm_encoder_enum_list[] = { DRM_MODE_ENCODER_DPMST, "DP MST" }, }; -static const struct drm_prop_enum_list drm_subpixel_enum_list[] = -{ +static const struct drm_prop_enum_list drm_subpixel_enum_list[] = { { SubPixelUnknown, "Unknown" }, { SubPixelHorizontalRGB, "Horizontal RGB" }, { SubPixelHorizontalBGR, "Horizontal BGR" }, @@ -725,6 +719,8 @@ void drm_crtc_cleanup(struct drm_crtc *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); + + memset(crtc, 0, sizeof(*crtc)); } EXPORT_SYMBOL(drm_crtc_cleanup); @@ -835,6 +831,7 @@ int drm_connector_init(struct drm_device *dev, const struct drm_connector_funcs *funcs, int connector_type) { + struct drm_mode_config *config = &dev->mode_config; int ret; struct ida *connector_ida = &drm_connector_enum_list[connector_type].ida; @@ -873,16 +870,20 @@ int drm_connector_init(struct drm_device *dev, /* 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++; + list_add_tail(&connector->head, &config->connector_list); + config->num_connector++; if (connector_type != DRM_MODE_CONNECTOR_VIRTUAL) drm_object_attach_property(&connector->base, - dev->mode_config.edid_property, + config->edid_property, 0); drm_object_attach_property(&connector->base, - dev->mode_config.dpms_property, 0); + config->dpms_property, 0); + + if (drm_core_check_feature(dev, DRIVER_ATOMIC)) { + drm_object_attach_property(&connector->base, config->prop_crtc_id, 0); + } connector->debugfs_entry = NULL; @@ -908,6 +909,11 @@ void drm_connector_cleanup(struct drm_connector *connector) struct drm_device *dev = connector->dev; struct drm_display_mode *mode, *t; + if (connector->tile_group) { + drm_mode_put_tile_group(dev, connector->tile_group); + connector->tile_group = NULL; + } + list_for_each_entry_safe(mode, t, &connector->probed_modes, head) drm_mode_remove(connector, mode); @@ -927,6 +933,8 @@ void drm_connector_cleanup(struct drm_connector *connector) if (connector->state && connector->funcs->atomic_destroy_state) connector->funcs->atomic_destroy_state(connector, connector->state); + + memset(connector, 0, sizeof(*connector)); } EXPORT_SYMBOL(drm_connector_cleanup); @@ -1068,6 +1076,8 @@ void drm_bridge_cleanup(struct drm_bridge *bridge) list_del(&bridge->head); dev->mode_config.num_bridge--; drm_modeset_unlock_all(dev); + + memset(bridge, 0, sizeof(*bridge)); } EXPORT_SYMBOL(drm_bridge_cleanup); @@ -1131,13 +1141,15 @@ EXPORT_SYMBOL(drm_encoder_init); void drm_encoder_cleanup(struct drm_encoder *encoder) { struct drm_device *dev = encoder->dev; + drm_modeset_lock_all(dev); drm_mode_object_put(dev, &encoder->base); kfree(encoder->name); - encoder->name = NULL; list_del(&encoder->head); dev->mode_config.num_encoder--; drm_modeset_unlock_all(dev); + + memset(encoder, 0, sizeof(*encoder)); } EXPORT_SYMBOL(drm_encoder_cleanup); @@ -1162,6 +1174,7 @@ int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane, const uint32_t *formats, uint32_t format_count, enum drm_plane_type type) { + struct drm_mode_config *config = &dev->mode_config; int ret; ret = drm_mode_object_get(dev, &plane->base, DRM_MODE_OBJECT_PLANE); @@ -1173,8 +1186,8 @@ int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane, plane->base.properties = &plane->properties; plane->dev = dev; plane->funcs = funcs; - plane->format_types = kmalloc(sizeof(uint32_t) * format_count, - GFP_KERNEL); + plane->format_types = kmalloc_array(format_count, sizeof(uint32_t), + GFP_KERNEL); if (!plane->format_types) { DRM_DEBUG_KMS("out of memory when allocating plane\n"); drm_mode_object_put(dev, &plane->base); @@ -1186,15 +1199,28 @@ int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane, plane->possible_crtcs = possible_crtcs; plane->type = type; - list_add_tail(&plane->head, &dev->mode_config.plane_list); - dev->mode_config.num_total_plane++; + list_add_tail(&plane->head, &config->plane_list); + config->num_total_plane++; if (plane->type == DRM_PLANE_TYPE_OVERLAY) - dev->mode_config.num_overlay_plane++; + config->num_overlay_plane++; drm_object_attach_property(&plane->base, - dev->mode_config.plane_type_property, + config->plane_type_property, plane->type); + if (drm_core_check_feature(dev, DRIVER_ATOMIC)) { + drm_object_attach_property(&plane->base, config->prop_fb_id, 0); + drm_object_attach_property(&plane->base, config->prop_crtc_id, 0); + drm_object_attach_property(&plane->base, config->prop_crtc_x, 0); + drm_object_attach_property(&plane->base, config->prop_crtc_y, 0); + drm_object_attach_property(&plane->base, config->prop_crtc_w, 0); + drm_object_attach_property(&plane->base, config->prop_crtc_h, 0); + drm_object_attach_property(&plane->base, config->prop_src_x, 0); + drm_object_attach_property(&plane->base, config->prop_src_y, 0); + drm_object_attach_property(&plane->base, config->prop_src_w, 0); + drm_object_attach_property(&plane->base, config->prop_src_h, 0); + } + return 0; } EXPORT_SYMBOL(drm_universal_plane_init); @@ -1257,6 +1283,8 @@ void drm_plane_cleanup(struct drm_plane *plane) 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); + + memset(plane, 0, sizeof(*plane)); } EXPORT_SYMBOL(drm_plane_cleanup); @@ -1314,45 +1342,109 @@ void drm_plane_force_disable(struct drm_plane *plane) } EXPORT_SYMBOL(drm_plane_force_disable); -static int drm_mode_create_standard_connector_properties(struct drm_device *dev) +static int drm_mode_create_standard_properties(struct drm_device *dev) { - struct drm_property *edid; - struct drm_property *dpms; - struct drm_property *dev_path; + struct drm_property *prop; /* * Standard properties (apply to all connectors) */ - edid = drm_property_create(dev, DRM_MODE_PROP_BLOB | + prop = drm_property_create(dev, DRM_MODE_PROP_BLOB | DRM_MODE_PROP_IMMUTABLE, "EDID", 0); - dev->mode_config.edid_property = edid; + if (!prop) + return -ENOMEM; + dev->mode_config.edid_property = prop; - dpms = drm_property_create_enum(dev, 0, + prop = drm_property_create_enum(dev, 0, "DPMS", drm_dpms_enum_list, ARRAY_SIZE(drm_dpms_enum_list)); - dev->mode_config.dpms_property = dpms; - - dev_path = drm_property_create(dev, - DRM_MODE_PROP_BLOB | - DRM_MODE_PROP_IMMUTABLE, - "PATH", 0); - dev->mode_config.path_property = dev_path; + if (!prop) + return -ENOMEM; + dev->mode_config.dpms_property = prop; - return 0; -} + prop = drm_property_create(dev, + DRM_MODE_PROP_BLOB | + DRM_MODE_PROP_IMMUTABLE, + "PATH", 0); + if (!prop) + return -ENOMEM; + dev->mode_config.path_property = prop; -static int drm_mode_create_standard_plane_properties(struct drm_device *dev) -{ - struct drm_property *type; + prop = drm_property_create(dev, + DRM_MODE_PROP_BLOB | + DRM_MODE_PROP_IMMUTABLE, + "TILE", 0); + if (!prop) + return -ENOMEM; + dev->mode_config.tile_property = prop; - /* - * Standard properties (apply to all planes) - */ - type = drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE, + prop = drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE, "type", drm_plane_type_enum_list, ARRAY_SIZE(drm_plane_type_enum_list)); - dev->mode_config.plane_type_property = type; + if (!prop) + return -ENOMEM; + dev->mode_config.plane_type_property = prop; + + prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC, + "SRC_X", 0, UINT_MAX); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_src_x = prop; + + prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC, + "SRC_Y", 0, UINT_MAX); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_src_y = prop; + + prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC, + "SRC_W", 0, UINT_MAX); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_src_w = prop; + + prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC, + "SRC_H", 0, UINT_MAX); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_src_h = prop; + + prop = drm_property_create_signed_range(dev, DRM_MODE_PROP_ATOMIC, + "CRTC_X", INT_MIN, INT_MAX); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_crtc_x = prop; + + prop = drm_property_create_signed_range(dev, DRM_MODE_PROP_ATOMIC, + "CRTC_Y", INT_MIN, INT_MAX); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_crtc_y = prop; + + prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC, + "CRTC_W", 0, INT_MAX); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_crtc_w = prop; + + prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC, + "CRTC_H", 0, INT_MAX); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_crtc_h = prop; + + prop = drm_property_create_object(dev, DRM_MODE_PROP_ATOMIC, + "FB_ID", DRM_MODE_OBJECT_FB); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_fb_id = prop; + + prop = drm_property_create_object(dev, DRM_MODE_PROP_ATOMIC, + "CRTC_ID", DRM_MODE_OBJECT_CRTC); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_crtc_id = prop; return 0; } @@ -1580,7 +1672,7 @@ static int drm_mode_group_init(struct drm_device *dev, struct drm_mode_group *gr total_objects += dev->mode_config.num_encoder; total_objects += dev->mode_config.num_bridge; - group->id_list = kzalloc(total_objects * sizeof(uint32_t), GFP_KERNEL); + group->id_list = kcalloc(total_objects, sizeof(uint32_t), GFP_KERNEL); if (!group->id_list) return -ENOMEM; @@ -1610,7 +1702,8 @@ int drm_mode_group_init_legacy_group(struct drm_device *dev, struct drm_bridge *bridge; int ret; - if ((ret = drm_mode_group_init(dev, group))) + ret = drm_mode_group_init(dev, group); + if (ret) return ret; list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) @@ -1977,6 +2070,44 @@ static struct drm_encoder *drm_connector_get_encoder(struct drm_connector *conne return connector->encoder; } +/* helper for getconnector and getproperties ioctls */ +static int get_properties(struct drm_mode_object *obj, bool atomic, + uint32_t __user *prop_ptr, uint64_t __user *prop_values, + uint32_t *arg_count_props) +{ + int props_count; + int i, ret, copied; + + props_count = obj->properties->count; + if (!atomic) + props_count -= obj->properties->atomic_count; + + if ((*arg_count_props >= props_count) && props_count) { + for (i = 0, copied = 0; copied < props_count; i++) { + struct drm_property *prop = obj->properties->properties[i]; + uint64_t val; + + if ((prop->flags & DRM_MODE_PROP_ATOMIC) && !atomic) + continue; + + ret = drm_object_property_get_value(obj, prop, &val); + if (ret) + return ret; + + if (put_user(prop->base.id, prop_ptr + copied)) + return -EFAULT; + + if (put_user(val, prop_values + copied)) + return -EFAULT; + + copied++; + } + } + *arg_count_props = props_count; + + return 0; +} + /** * drm_mode_getconnector - get connector configuration * @dev: drm device for the ioctl @@ -1998,15 +2129,12 @@ int drm_mode_getconnector(struct drm_device *dev, void *data, struct drm_encoder *encoder; struct drm_display_mode *mode; int mode_count = 0; - int props_count = 0; int encoders_count = 0; int ret = 0; int copied = 0; int i; struct drm_mode_modeinfo u_mode; struct drm_mode_modeinfo __user *mode_ptr; - uint32_t __user *prop_ptr; - uint64_t __user *prop_values; uint32_t __user *encoder_ptr; if (!drm_core_check_feature(dev, DRIVER_MODESET)) @@ -2017,6 +2145,7 @@ int drm_mode_getconnector(struct drm_device *dev, void *data, DRM_DEBUG_KMS("[CONNECTOR:%d:?]\n", out_resp->connector_id); mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); connector = drm_connector_find(dev, out_resp->connector_id); if (!connector) { @@ -2024,13 +2153,9 @@ int drm_mode_getconnector(struct drm_device *dev, void *data, goto out; } - props_count = connector->properties.count; - - for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { - if (connector->encoder_ids[i] != 0) { + for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) + if (connector->encoder_ids[i] != 0) encoders_count++; - } - } if (out_resp->count_modes == 0) { connector->funcs->fill_modes(connector, @@ -2050,14 +2175,11 @@ int drm_mode_getconnector(struct drm_device *dev, void *data, out_resp->mm_height = connector->display_info.height_mm; out_resp->subpixel = connector->display_info.subpixel_order; out_resp->connection = connector->status; - drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); - 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); /* * This ioctl is called twice, once to determine how much space is @@ -2081,26 +2203,12 @@ int drm_mode_getconnector(struct drm_device *dev, void *data, } out_resp->count_modes = mode_count; - if ((out_resp->count_props >= props_count) && props_count) { - copied = 0; - prop_ptr = (uint32_t __user *)(unsigned long)(out_resp->props_ptr); - prop_values = (uint64_t __user *)(unsigned long)(out_resp->prop_values_ptr); - for (i = 0; i < connector->properties.count; i++) { - if (put_user(connector->properties.ids[i], - prop_ptr + copied)) { - ret = -EFAULT; - goto out; - } - - if (put_user(connector->properties.values[i], - prop_values + copied)) { - ret = -EFAULT; - goto out; - } - copied++; - } - } - out_resp->count_props = props_count; + ret = get_properties(&connector->base, file_priv->atomic, + (uint32_t __user *)(unsigned long)(out_resp->props_ptr), + (uint64_t __user *)(unsigned long)(out_resp->prop_values_ptr), + &out_resp->count_props); + if (ret) + goto out; if ((out_resp->count_encoders >= encoders_count) && encoders_count) { copied = 0; @@ -2119,6 +2227,7 @@ int drm_mode_getconnector(struct drm_device *dev, void *data, out_resp->count_encoders = encoders_count; out: + drm_modeset_unlock(&dev->mode_config.connection_mutex); mutex_unlock(&dev->mode_config.mutex); return ret; @@ -2510,7 +2619,7 @@ int drm_mode_setplane(struct drm_device *dev, void *data, * * This is a little helper to wrap internal calls to the ->set_config driver * interface. The only thing it adds is correct refcounting dance. - * + * * Returns: * Zero on success, negative errno on failure. */ @@ -2683,6 +2792,12 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, goto out; } + mode->status = drm_mode_validate_basic(mode); + if (mode->status != MODE_OK) { + ret = -EINVAL; + goto out; + } + drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); ret = drm_crtc_check_viewport(crtc, crtc_req->x, crtc_req->y, @@ -2714,9 +2829,9 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, goto out; } - connector_set = kmalloc(crtc_req->count_connectors * - sizeof(struct drm_connector *), - GFP_KERNEL); + connector_set = kmalloc_array(crtc_req->count_connectors, + sizeof(struct drm_connector *), + GFP_KERNEL); if (!connector_set) { ret = -ENOMEM; goto out; @@ -2961,6 +3076,7 @@ int drm_mode_cursor2_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_cursor2 *req = data; + return drm_mode_cursor_common(dev, req, file_priv); } @@ -3408,7 +3524,7 @@ int drm_mode_dirtyfb_ioctl(struct drm_device *dev, ret = -EINVAL; goto out_err1; } - clips = kzalloc(num_clips * sizeof(*clips), GFP_KERNEL); + clips = kcalloc(num_clips, sizeof(*clips), GFP_KERNEL); if (!clips) { ret = -ENOMEM; goto out_err1; @@ -3456,7 +3572,7 @@ void drm_fb_release(struct drm_file *priv) /* * When the file gets released that means no one else can access the fb - * list any more, so no need to grab fpriv->fbs_lock. And we need to to + * list any more, so no need to grab fpriv->fbs_lock. And we need to * avoid upsetting lockdep since the universal cursor code adds a * framebuffer while holding mutex locks. * @@ -3509,7 +3625,8 @@ struct drm_property *drm_property_create(struct drm_device *dev, int flags, property->dev = dev; if (num_values) { - property->values = kzalloc(sizeof(uint64_t)*num_values, GFP_KERNEL); + property->values = kcalloc(num_values, sizeof(uint64_t), + GFP_KERNEL); if (!property->values) goto fail; } @@ -3815,9 +3932,11 @@ void drm_object_attach_property(struct drm_mode_object *obj, return; } - obj->properties->ids[count] = property->base.id; + obj->properties->properties[count] = property; obj->properties->values[count] = init_val; obj->properties->count++; + if (property->flags & DRM_MODE_PROP_ATOMIC) + obj->properties->atomic_count++; } EXPORT_SYMBOL(drm_object_attach_property); @@ -3840,7 +3959,7 @@ int drm_object_property_set_value(struct drm_mode_object *obj, int i; for (i = 0; i < obj->properties->count; i++) { - if (obj->properties->ids[i] == property->base.id) { + if (obj->properties->properties[i] == property) { obj->properties->values[i] = val; return 0; } @@ -3869,8 +3988,16 @@ int drm_object_property_get_value(struct drm_mode_object *obj, { int i; + /* read-only properties bypass atomic mechanism and still store + * their value in obj->properties->values[].. mostly to avoid + * having to deal w/ EDID and similar props in atomic paths: + */ + if (drm_core_check_feature(property->dev, DRIVER_ATOMIC) && + !(property->flags & DRM_MODE_PROP_IMMUTABLE)) + return drm_atomic_get_property(obj, property, val); + for (i = 0; i < obj->properties->count; i++) { - if (obj->properties->ids[i] == property->base.id) { + if (obj->properties->properties[i] == property) { *val = obj->properties->values[i]; return 0; } @@ -4050,7 +4177,7 @@ int drm_mode_getblob_ioctl(struct drm_device *dev, if (out_resp->length == blob->length) { blob_ptr = (void __user *)(unsigned long)out_resp->data; - if (copy_to_user(blob_ptr, blob->data, blob->length)){ + if (copy_to_user(blob_ptr, blob->data, blob->length)) { ret = -EFAULT; goto done; } @@ -4094,6 +4221,52 @@ int drm_mode_connector_set_path_property(struct drm_connector *connector, } EXPORT_SYMBOL(drm_mode_connector_set_path_property); +/** + * drm_mode_connector_set_tile_property - set tile property on connector + * @connector: connector to set property on. + * + * This looks up the tile information for a connector, and creates a + * property for userspace to parse if it exists. The property is of + * the form of 8 integers using ':' as a separator. + * + * Returns: + * Zero on success, errno on failure. + */ +int drm_mode_connector_set_tile_property(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + int ret, size; + char tile[256]; + + if (connector->tile_blob_ptr) + drm_property_destroy_blob(dev, connector->tile_blob_ptr); + + if (!connector->has_tile) { + connector->tile_blob_ptr = NULL; + ret = drm_object_property_set_value(&connector->base, + dev->mode_config.tile_property, 0); + return ret; + } + + snprintf(tile, 256, "%d:%d:%d:%d:%d:%d:%d:%d", + connector->tile_group->id, connector->tile_is_single_monitor, + connector->num_h_tile, connector->num_v_tile, + connector->tile_h_loc, connector->tile_v_loc, + connector->tile_h_size, connector->tile_v_size); + size = strlen(tile) + 1; + + connector->tile_blob_ptr = drm_property_create_blob(connector->dev, + size, tile); + if (!connector->tile_blob_ptr) + return -EINVAL; + + ret = drm_object_property_set_value(&connector->base, + dev->mode_config.tile_property, + connector->tile_blob_ptr->base.id); + return ret; +} +EXPORT_SYMBOL(drm_mode_connector_set_tile_property); + /** * drm_mode_connector_update_edid_property - update the edid property of a connector * @connector: drm connector @@ -4140,25 +4313,38 @@ int drm_mode_connector_update_edid_property(struct drm_connector *connector, } EXPORT_SYMBOL(drm_mode_connector_update_edid_property); -static bool drm_property_change_is_valid(struct drm_property *property, - uint64_t value) +/* Some properties could refer to dynamic refcnt'd objects, or things that + * need special locking to handle lifetime issues (ie. to ensure the prop + * value doesn't become invalid part way through the property update due to + * race). The value returned by reference via 'obj' should be passed back + * to drm_property_change_valid_put() after the property is set (and the + * object to which the property is attached has a chance to take it's own + * reference). + */ +bool drm_property_change_valid_get(struct drm_property *property, + uint64_t value, struct drm_mode_object **ref) { + int i; + if (property->flags & DRM_MODE_PROP_IMMUTABLE) return false; + *ref = NULL; + if (drm_property_type_is(property, DRM_MODE_PROP_RANGE)) { if (value < property->values[0] || value > property->values[1]) return false; return true; } else if (drm_property_type_is(property, DRM_MODE_PROP_SIGNED_RANGE)) { int64_t svalue = U642I64(value); + if (svalue < U642I64(property->values[0]) || svalue > U642I64(property->values[1])) return false; return true; } else if (drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) { - int i; uint64_t valid_mask = 0; + for (i = 0; i < property->num_values; i++) valid_mask |= (1ULL << property->values[i]); return !(value & ~valid_mask); @@ -4166,19 +4352,23 @@ static bool drm_property_change_is_valid(struct drm_property *property, /* Only the driver knows */ return true; } else if (drm_property_type_is(property, DRM_MODE_PROP_OBJECT)) { - struct drm_mode_object *obj; /* a zero value for an object property translates to null: */ if (value == 0) return true; - /* - * NOTE: use _object_find() directly to bypass restriction on - * looking up refcnt'd objects (ie. fb's). For a refcnt'd - * object this could race against object finalization, so it - * simply tells us that the object *was* valid. Which is good - * enough. - */ - obj = _object_find(property->dev, value, property->values[0]); - return obj != NULL; + + /* handle refcnt'd objects specially: */ + if (property->values[0] == DRM_MODE_OBJECT_FB) { + struct drm_framebuffer *fb; + fb = drm_framebuffer_lookup(property->dev, value); + if (fb) { + *ref = &fb->base; + return true; + } else { + return false; + } + } else { + return _object_find(property->dev, value, property->values[0]) != NULL; + } } else { int i; for (i = 0; i < property->num_values; i++) @@ -4186,6 +4376,23 @@ static bool drm_property_change_is_valid(struct drm_property *property, return true; return false; } + + for (i = 0; i < property->num_values; i++) + if (property->values[i] == value) + return true; + return false; +} + +void drm_property_change_valid_put(struct drm_property *property, + struct drm_mode_object *ref) +{ + if (!ref) + return; + + if (drm_property_type_is(property, DRM_MODE_PROP_OBJECT)) { + if (property->values[0] == DRM_MODE_OBJECT_FB) + drm_framebuffer_unreference(obj_to_fb(ref)); + } } /** @@ -4303,11 +4510,6 @@ int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data, struct drm_mode_obj_get_properties *arg = data; struct drm_mode_object *obj; int ret = 0; - int i; - int copied = 0; - int props_count = 0; - uint32_t __user *props_ptr; - uint64_t __user *prop_values_ptr; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; @@ -4324,30 +4526,11 @@ int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data, goto out; } - props_count = obj->properties->count; + ret = get_properties(obj, file_priv->atomic, + (uint32_t __user *)(unsigned long)(arg->props_ptr), + (uint64_t __user *)(unsigned long)(arg->prop_values_ptr), + &arg->count_props); - /* This ioctl is called twice, once to determine how much space is - * needed, and the 2nd time to fill it. */ - if ((arg->count_props >= props_count) && props_count) { - copied = 0; - props_ptr = (uint32_t __user *)(unsigned long)(arg->props_ptr); - prop_values_ptr = (uint64_t __user *)(unsigned long) - (arg->prop_values_ptr); - for (i = 0; i < props_count; i++) { - if (put_user(obj->properties->ids[i], - props_ptr + copied)) { - ret = -EFAULT; - goto out; - } - if (put_user(obj->properties->values[i], - prop_values_ptr + copied)) { - ret = -EFAULT; - goto out; - } - copied++; - } - } - arg->count_props = props_count; out: drm_modeset_unlock_all(dev); return ret; @@ -4376,8 +4559,8 @@ int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data, struct drm_mode_object *arg_obj; struct drm_mode_object *prop_obj; struct drm_property *property; - int ret = -EINVAL; - int i; + int i, ret = -EINVAL; + struct drm_mode_object *ref; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; @@ -4393,7 +4576,7 @@ int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data, goto out; for (i = 0; i < arg_obj->properties->count; i++) - if (arg_obj->properties->ids[i] == arg->prop_id) + if (arg_obj->properties->properties[i]->base.id == arg->prop_id) break; if (i == arg_obj->properties->count) @@ -4407,7 +4590,7 @@ int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data, } property = obj_to_property(prop_obj); - if (!drm_property_change_is_valid(property, arg->value)) + if (!drm_property_change_valid_get(property, arg->value, &ref)) goto out; switch (arg_obj->type) { @@ -4424,6 +4607,8 @@ int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data, break; } + drm_property_change_valid_put(property, ref); + out: drm_modeset_unlock_all(dev); return ret; @@ -4473,7 +4658,8 @@ int drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc, { crtc->gamma_size = gamma_size; - crtc->gamma_store = kzalloc(gamma_size * sizeof(uint16_t) * 3, GFP_KERNEL); + crtc->gamma_store = kcalloc(gamma_size, sizeof(uint16_t) * 3, + GFP_KERNEL); if (!crtc->gamma_store) { crtc->gamma_size = 0; return -ENOMEM; @@ -4688,23 +4874,23 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) { ret = -ENOMEM; spin_lock_irqsave(&dev->event_lock, flags); - if (file_priv->event_space < sizeof e->event) { + if (file_priv->event_space < sizeof(e->event)) { spin_unlock_irqrestore(&dev->event_lock, flags); goto out; } - file_priv->event_space -= sizeof e->event; + file_priv->event_space -= sizeof(e->event); spin_unlock_irqrestore(&dev->event_lock, flags); - e = kzalloc(sizeof *e, GFP_KERNEL); + e = kzalloc(sizeof(*e), GFP_KERNEL); if (e == NULL) { spin_lock_irqsave(&dev->event_lock, flags); - file_priv->event_space += sizeof e->event; + file_priv->event_space += sizeof(e->event); spin_unlock_irqrestore(&dev->event_lock, flags); goto out; } e->event.base.type = DRM_EVENT_FLIP_COMPLETE; - e->event.base.length = sizeof e->event; + e->event.base.length = sizeof(e->event); e->event.user_data = page_flip->user_data; e->base.event = &e->event.base; e->base.file_priv = file_priv; @@ -4717,7 +4903,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, if (ret) { if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) { spin_lock_irqsave(&dev->event_lock, flags); - file_priv->event_space += sizeof e->event; + file_priv->event_space += sizeof(e->event); spin_unlock_irqrestore(&dev->event_lock, flags); kfree(e); } @@ -5164,10 +5350,10 @@ void drm_mode_config_init(struct drm_device *dev) INIT_LIST_HEAD(&dev->mode_config.property_blob_list); INIT_LIST_HEAD(&dev->mode_config.plane_list); idr_init(&dev->mode_config.crtc_idr); + idr_init(&dev->mode_config.tile_idr); drm_modeset_lock_all(dev); - drm_mode_create_standard_connector_properties(dev); - drm_mode_create_standard_plane_properties(dev); + drm_mode_create_standard_properties(dev); drm_modeset_unlock_all(dev); /* Just to be sure */ @@ -5251,6 +5437,7 @@ void drm_mode_config_cleanup(struct drm_device *dev) crtc->funcs->destroy(crtc); } + idr_destroy(&dev->mode_config.tile_idr); idr_destroy(&dev->mode_config.crtc_idr); drm_modeset_lock_fini(&dev->mode_config.connection_mutex); } @@ -5273,3 +5460,100 @@ struct drm_property *drm_mode_create_rotation_property(struct drm_device *dev, supported_rotations); } EXPORT_SYMBOL(drm_mode_create_rotation_property); + +/** + * DOC: Tile group + * + * Tile groups are used to represent tiled monitors with a unique + * integer identifier. Tiled monitors using DisplayID v1.3 have + * a unique 8-byte handle, we store this in a tile group, so we + * have a common identifier for all tiles in a monitor group. + */ +static void drm_tile_group_free(struct kref *kref) +{ + struct drm_tile_group *tg = container_of(kref, struct drm_tile_group, refcount); + struct drm_device *dev = tg->dev; + mutex_lock(&dev->mode_config.idr_mutex); + idr_remove(&dev->mode_config.tile_idr, tg->id); + mutex_unlock(&dev->mode_config.idr_mutex); + kfree(tg); +} + +/** + * drm_mode_put_tile_group - drop a reference to a tile group. + * @dev: DRM device + * @tg: tile group to drop reference to. + * + * drop reference to tile group and free if 0. + */ +void drm_mode_put_tile_group(struct drm_device *dev, + struct drm_tile_group *tg) +{ + kref_put(&tg->refcount, drm_tile_group_free); +} + +/** + * drm_mode_get_tile_group - get a reference to an existing tile group + * @dev: DRM device + * @topology: 8-bytes unique per monitor. + * + * Use the unique bytes to get a reference to an existing tile group. + * + * RETURNS: + * tile group or NULL if not found. + */ +struct drm_tile_group *drm_mode_get_tile_group(struct drm_device *dev, + char topology[8]) +{ + struct drm_tile_group *tg; + int id; + mutex_lock(&dev->mode_config.idr_mutex); + idr_for_each_entry(&dev->mode_config.tile_idr, tg, id) { + if (!memcmp(tg->group_data, topology, 8)) { + if (!kref_get_unless_zero(&tg->refcount)) + tg = NULL; + mutex_unlock(&dev->mode_config.idr_mutex); + return tg; + } + } + mutex_unlock(&dev->mode_config.idr_mutex); + return NULL; +} + +/** + * drm_mode_create_tile_group - create a tile group from a displayid description + * @dev: DRM device + * @topology: 8-bytes unique per monitor. + * + * Create a tile group for the unique monitor, and get a unique + * identifier for the tile group. + * + * RETURNS: + * new tile group or error. + */ +struct drm_tile_group *drm_mode_create_tile_group(struct drm_device *dev, + char topology[8]) +{ + struct drm_tile_group *tg; + int ret; + + tg = kzalloc(sizeof(*tg), GFP_KERNEL); + if (!tg) + return ERR_PTR(-ENOMEM); + + kref_init(&tg->refcount); + memcpy(tg->group_data, topology, 8); + tg->dev = dev; + + mutex_lock(&dev->mode_config.idr_mutex); + ret = idr_alloc(&dev->mode_config.tile_idr, tg, 1, 0, GFP_KERNEL); + if (ret >= 0) { + tg->id = ret; + } else { + kfree(tg); + tg = ERR_PTR(ret); + } + + mutex_unlock(&dev->mode_config.idr_mutex); + return tg; +}