summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/drm_crtc.c
diff options
context:
space:
mode:
authorIngo Molnar <mingo@kernel.org>2012-04-14 15:18:27 +0400
committerIngo Molnar <mingo@kernel.org>2012-04-14 15:19:04 +0400
commit6ac1ef482d7ae0c690f1640bf6eb818ff9a2d91e (patch)
tree021cc9f6b477146fcebe6f3be4752abfa2ba18a9 /drivers/gpu/drm/drm_crtc.c
parent682968e0c425c60f0dde37977e5beb2b12ddc4cc (diff)
parenta385ec4f11bdcf81af094c03e2444ee9b7fad2e5 (diff)
downloadlinux-6ac1ef482d7ae0c690f1640bf6eb818ff9a2d91e.tar.xz
Merge branch 'perf/core' into perf/uprobes
Merge in latest upstream (and the latest perf development tree), to prepare for tooling changes, and also to pick up v3.4 MM changes that the uprobes code needs to take care of. Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'drivers/gpu/drm/drm_crtc.c')
-rw-r--r--drivers/gpu/drm/drm_crtc.c448
1 files changed, 288 insertions, 160 deletions
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 5e818a808ace..d3aaeb6ae236 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -38,11 +38,6 @@
#include "drm_edid.h"
#include "drm_fourcc.h"
-struct drm_prop_enum_list {
- int type;
- char *name;
-};
-
/* Avoid boilerplate. I'm tired of typing. */
#define DRM_ENUM_NAME_FN(fnname, list) \
char *fnname(int val) \
@@ -298,9 +293,8 @@ int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb,
int ret;
ret = drm_mode_object_get(dev, &fb->base, DRM_MODE_OBJECT_FB);
- if (ret) {
+ if (ret)
return ret;
- }
fb->dev = dev;
fb->funcs = funcs;
@@ -370,19 +364,31 @@ EXPORT_SYMBOL(drm_framebuffer_cleanup);
* Caller must hold mode config lock.
*
* Inits a new object created as base part of an driver crtc object.
+ *
+ * RETURNS:
+ * Zero on success, error code on failure.
*/
-void drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
+int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
const struct drm_crtc_funcs *funcs)
{
+ int ret;
+
crtc->dev = dev;
crtc->funcs = funcs;
mutex_lock(&dev->mode_config.mutex);
- drm_mode_object_get(dev, &crtc->base, DRM_MODE_OBJECT_CRTC);
+
+ ret = drm_mode_object_get(dev, &crtc->base, DRM_MODE_OBJECT_CRTC);
+ if (ret)
+ goto out;
list_add_tail(&crtc->head, &dev->mode_config.crtc_list);
dev->mode_config.num_crtc++;
+
+ out:
mutex_unlock(&dev->mode_config.mutex);
+
+ return ret;
}
EXPORT_SYMBOL(drm_crtc_init);
@@ -442,7 +448,7 @@ void drm_mode_remove(struct drm_connector *connector,
struct drm_display_mode *mode)
{
list_del(&mode->head);
- kfree(mode);
+ drm_mode_destroy(connector->dev, mode);
}
EXPORT_SYMBOL(drm_mode_remove);
@@ -454,21 +460,29 @@ EXPORT_SYMBOL(drm_mode_remove);
* @name: user visible name of the connector
*
* LOCKING:
- * Caller must hold @dev's mode_config lock.
+ * Takes mode config lock.
*
* Initialises a preallocated connector. Connectors should be
* subclassed as part of driver connector objects.
+ *
+ * RETURNS:
+ * Zero on success, error code on failure.
*/
-void drm_connector_init(struct drm_device *dev,
- struct drm_connector *connector,
- const struct drm_connector_funcs *funcs,
- int connector_type)
+int drm_connector_init(struct drm_device *dev,
+ struct drm_connector *connector,
+ const struct drm_connector_funcs *funcs,
+ int connector_type)
{
+ int ret;
+
mutex_lock(&dev->mode_config.mutex);
+ ret = drm_mode_object_get(dev, &connector->base, DRM_MODE_OBJECT_CONNECTOR);
+ if (ret)
+ goto out;
+
connector->dev = dev;
connector->funcs = funcs;
- drm_mode_object_get(dev, &connector->base, DRM_MODE_OBJECT_CONNECTOR);
connector->connector_type = connector_type;
connector->connector_type_id =
++drm_connector_enum_list[connector_type].count; /* TODO */
@@ -488,7 +502,10 @@ void drm_connector_init(struct drm_device *dev,
drm_connector_attach_property(connector,
dev->mode_config.dpms_property, 0);
+ out:
mutex_unlock(&dev->mode_config.mutex);
+
+ return ret;
}
EXPORT_SYMBOL(drm_connector_init);
@@ -497,7 +514,7 @@ EXPORT_SYMBOL(drm_connector_init);
* @connector: connector to cleanup
*
* LOCKING:
- * Caller must hold @dev's mode_config lock.
+ * Takes mode config lock.
*
* Cleans up the connector but doesn't free the object.
*/
@@ -523,23 +540,41 @@ void drm_connector_cleanup(struct drm_connector *connector)
}
EXPORT_SYMBOL(drm_connector_cleanup);
-void drm_encoder_init(struct drm_device *dev,
+void drm_connector_unplug_all(struct drm_device *dev)
+{
+ struct drm_connector *connector;
+
+ /* taking the mode config mutex ends up in a clash with sysfs */
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head)
+ drm_sysfs_connector_remove(connector);
+
+}
+EXPORT_SYMBOL(drm_connector_unplug_all);
+
+int drm_encoder_init(struct drm_device *dev,
struct drm_encoder *encoder,
const struct drm_encoder_funcs *funcs,
int encoder_type)
{
+ int ret;
+
mutex_lock(&dev->mode_config.mutex);
- encoder->dev = dev;
+ ret = drm_mode_object_get(dev, &encoder->base, DRM_MODE_OBJECT_ENCODER);
+ if (ret)
+ goto out;
- drm_mode_object_get(dev, &encoder->base, DRM_MODE_OBJECT_ENCODER);
+ encoder->dev = dev;
encoder->encoder_type = encoder_type;
encoder->funcs = funcs;
list_add_tail(&encoder->head, &dev->mode_config.encoder_list);
dev->mode_config.num_encoder++;
+ out:
mutex_unlock(&dev->mode_config.mutex);
+
+ return ret;
}
EXPORT_SYMBOL(drm_encoder_init);
@@ -560,18 +595,23 @@ int drm_plane_init(struct drm_device *dev, struct drm_plane *plane,
const uint32_t *formats, uint32_t format_count,
bool priv)
{
+ int ret;
+
mutex_lock(&dev->mode_config.mutex);
+ ret = drm_mode_object_get(dev, &plane->base, DRM_MODE_OBJECT_PLANE);
+ if (ret)
+ goto out;
+
plane->dev = dev;
- drm_mode_object_get(dev, &plane->base, DRM_MODE_OBJECT_PLANE);
plane->funcs = funcs;
plane->format_types = kmalloc(sizeof(uint32_t) * format_count,
GFP_KERNEL);
if (!plane->format_types) {
DRM_DEBUG_KMS("out of memory when allocating plane\n");
drm_mode_object_put(dev, &plane->base);
- mutex_unlock(&dev->mode_config.mutex);
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto out;
}
memcpy(plane->format_types, formats, format_count * sizeof(uint32_t));
@@ -589,9 +629,10 @@ int drm_plane_init(struct drm_device *dev, struct drm_plane *plane,
INIT_LIST_HEAD(&plane->head);
}
+ out:
mutex_unlock(&dev->mode_config.mutex);
- return 0;
+ return ret;
}
EXPORT_SYMBOL(drm_plane_init);
@@ -631,7 +672,11 @@ struct drm_display_mode *drm_mode_create(struct drm_device *dev)
if (!nmode)
return NULL;
- drm_mode_object_get(dev, &nmode->base, DRM_MODE_OBJECT_MODE);
+ if (drm_mode_object_get(dev, &nmode->base, DRM_MODE_OBJECT_MODE)) {
+ kfree(nmode);
+ return NULL;
+ }
+
return nmode;
}
EXPORT_SYMBOL(drm_mode_create);
@@ -648,6 +693,9 @@ EXPORT_SYMBOL(drm_mode_create);
*/
void drm_mode_destroy(struct drm_device *dev, struct drm_display_mode *mode)
{
+ if (!mode)
+ return;
+
drm_mode_object_put(dev, &mode->base);
kfree(mode);
@@ -658,7 +706,6 @@ static int drm_mode_create_standard_connector_properties(struct drm_device *dev)
{
struct drm_property *edid;
struct drm_property *dpms;
- int i;
/*
* Standard properties (apply to all connectors)
@@ -668,11 +715,9 @@ static int drm_mode_create_standard_connector_properties(struct drm_device *dev)
"EDID", 0);
dev->mode_config.edid_property = edid;
- dpms = drm_property_create(dev, DRM_MODE_PROP_ENUM,
- "DPMS", ARRAY_SIZE(drm_dpms_enum_list));
- for (i = 0; i < ARRAY_SIZE(drm_dpms_enum_list); i++)
- drm_property_add_enum(dpms, i, drm_dpms_enum_list[i].type,
- drm_dpms_enum_list[i].name);
+ dpms = drm_property_create_enum(dev, 0,
+ "DPMS", drm_dpms_enum_list,
+ ARRAY_SIZE(drm_dpms_enum_list));
dev->mode_config.dpms_property = dpms;
return 0;
@@ -688,30 +733,21 @@ int drm_mode_create_dvi_i_properties(struct drm_device *dev)
{
struct drm_property *dvi_i_selector;
struct drm_property *dvi_i_subconnector;
- int i;
if (dev->mode_config.dvi_i_select_subconnector_property)
return 0;
dvi_i_selector =
- drm_property_create(dev, DRM_MODE_PROP_ENUM,
+ drm_property_create_enum(dev, 0,
"select subconnector",
+ drm_dvi_i_select_enum_list,
ARRAY_SIZE(drm_dvi_i_select_enum_list));
- for (i = 0; i < ARRAY_SIZE(drm_dvi_i_select_enum_list); i++)
- drm_property_add_enum(dvi_i_selector, i,
- drm_dvi_i_select_enum_list[i].type,
- drm_dvi_i_select_enum_list[i].name);
dev->mode_config.dvi_i_select_subconnector_property = dvi_i_selector;
- dvi_i_subconnector =
- drm_property_create(dev, DRM_MODE_PROP_ENUM |
- DRM_MODE_PROP_IMMUTABLE,
+ dvi_i_subconnector = drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE,
"subconnector",
+ drm_dvi_i_subconnector_enum_list,
ARRAY_SIZE(drm_dvi_i_subconnector_enum_list));
- for (i = 0; i < ARRAY_SIZE(drm_dvi_i_subconnector_enum_list); i++)
- drm_property_add_enum(dvi_i_subconnector, i,
- drm_dvi_i_subconnector_enum_list[i].type,
- drm_dvi_i_subconnector_enum_list[i].name);
dev->mode_config.dvi_i_subconnector_property = dvi_i_subconnector;
return 0;
@@ -742,51 +778,33 @@ int drm_mode_create_tv_properties(struct drm_device *dev, int num_modes,
/*
* Basic connector properties
*/
- tv_selector = drm_property_create(dev, DRM_MODE_PROP_ENUM,
+ tv_selector = drm_property_create_enum(dev, 0,
"select subconnector",
+ drm_tv_select_enum_list,
ARRAY_SIZE(drm_tv_select_enum_list));
- for (i = 0; i < ARRAY_SIZE(drm_tv_select_enum_list); i++)
- drm_property_add_enum(tv_selector, i,
- drm_tv_select_enum_list[i].type,
- drm_tv_select_enum_list[i].name);
dev->mode_config.tv_select_subconnector_property = tv_selector;
tv_subconnector =
- drm_property_create(dev, DRM_MODE_PROP_ENUM |
- DRM_MODE_PROP_IMMUTABLE, "subconnector",
+ drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE,
+ "subconnector",
+ drm_tv_subconnector_enum_list,
ARRAY_SIZE(drm_tv_subconnector_enum_list));
- for (i = 0; i < ARRAY_SIZE(drm_tv_subconnector_enum_list); i++)
- drm_property_add_enum(tv_subconnector, i,
- drm_tv_subconnector_enum_list[i].type,
- drm_tv_subconnector_enum_list[i].name);
dev->mode_config.tv_subconnector_property = tv_subconnector;
/*
* Other, TV specific properties: margins & TV modes.
*/
dev->mode_config.tv_left_margin_property =
- drm_property_create(dev, DRM_MODE_PROP_RANGE,
- "left margin", 2);
- dev->mode_config.tv_left_margin_property->values[0] = 0;
- dev->mode_config.tv_left_margin_property->values[1] = 100;
+ drm_property_create_range(dev, 0, "left margin", 0, 100);
dev->mode_config.tv_right_margin_property =
- drm_property_create(dev, DRM_MODE_PROP_RANGE,
- "right margin", 2);
- dev->mode_config.tv_right_margin_property->values[0] = 0;
- dev->mode_config.tv_right_margin_property->values[1] = 100;
+ drm_property_create_range(dev, 0, "right margin", 0, 100);
dev->mode_config.tv_top_margin_property =
- drm_property_create(dev, DRM_MODE_PROP_RANGE,
- "top margin", 2);
- dev->mode_config.tv_top_margin_property->values[0] = 0;
- dev->mode_config.tv_top_margin_property->values[1] = 100;
+ drm_property_create_range(dev, 0, "top margin", 0, 100);
dev->mode_config.tv_bottom_margin_property =
- drm_property_create(dev, DRM_MODE_PROP_RANGE,
- "bottom margin", 2);
- dev->mode_config.tv_bottom_margin_property->values[0] = 0;
- dev->mode_config.tv_bottom_margin_property->values[1] = 100;
+ drm_property_create_range(dev, 0, "bottom margin", 0, 100);
dev->mode_config.tv_mode_property =
drm_property_create(dev, DRM_MODE_PROP_ENUM,
@@ -796,40 +814,22 @@ int drm_mode_create_tv_properties(struct drm_device *dev, int num_modes,
i, modes[i]);
dev->mode_config.tv_brightness_property =
- drm_property_create(dev, DRM_MODE_PROP_RANGE,
- "brightness", 2);
- dev->mode_config.tv_brightness_property->values[0] = 0;
- dev->mode_config.tv_brightness_property->values[1] = 100;
+ drm_property_create_range(dev, 0, "brightness", 0, 100);
dev->mode_config.tv_contrast_property =
- drm_property_create(dev, DRM_MODE_PROP_RANGE,
- "contrast", 2);
- dev->mode_config.tv_contrast_property->values[0] = 0;
- dev->mode_config.tv_contrast_property->values[1] = 100;
+ drm_property_create_range(dev, 0, "contrast", 0, 100);
dev->mode_config.tv_flicker_reduction_property =
- drm_property_create(dev, DRM_MODE_PROP_RANGE,
- "flicker reduction", 2);
- dev->mode_config.tv_flicker_reduction_property->values[0] = 0;
- dev->mode_config.tv_flicker_reduction_property->values[1] = 100;
+ drm_property_create_range(dev, 0, "flicker reduction", 0, 100);
dev->mode_config.tv_overscan_property =
- drm_property_create(dev, DRM_MODE_PROP_RANGE,
- "overscan", 2);
- dev->mode_config.tv_overscan_property->values[0] = 0;
- dev->mode_config.tv_overscan_property->values[1] = 100;
+ drm_property_create_range(dev, 0, "overscan", 0, 100);
dev->mode_config.tv_saturation_property =
- drm_property_create(dev, DRM_MODE_PROP_RANGE,
- "saturation", 2);
- dev->mode_config.tv_saturation_property->values[0] = 0;
- dev->mode_config.tv_saturation_property->values[1] = 100;
+ drm_property_create_range(dev, 0, "saturation", 0, 100);
dev->mode_config.tv_hue_property =
- drm_property_create(dev, DRM_MODE_PROP_RANGE,
- "hue", 2);
- dev->mode_config.tv_hue_property->values[0] = 0;
- dev->mode_config.tv_hue_property->values[1] = 100;
+ drm_property_create_range(dev, 0, "hue", 0, 100);
return 0;
}
@@ -845,18 +845,14 @@ EXPORT_SYMBOL(drm_mode_create_tv_properties);
int drm_mode_create_scaling_mode_property(struct drm_device *dev)
{
struct drm_property *scaling_mode;
- int i;
if (dev->mode_config.scaling_mode_property)
return 0;
scaling_mode =
- drm_property_create(dev, DRM_MODE_PROP_ENUM, "scaling mode",
+ drm_property_create_enum(dev, 0, "scaling mode",
+ drm_scaling_mode_enum_list,
ARRAY_SIZE(drm_scaling_mode_enum_list));
- for (i = 0; i < ARRAY_SIZE(drm_scaling_mode_enum_list); i++)
- drm_property_add_enum(scaling_mode, i,
- drm_scaling_mode_enum_list[i].type,
- drm_scaling_mode_enum_list[i].name);
dev->mode_config.scaling_mode_property = scaling_mode;
@@ -874,18 +870,14 @@ EXPORT_SYMBOL(drm_mode_create_scaling_mode_property);
int drm_mode_create_dithering_property(struct drm_device *dev)
{
struct drm_property *dithering_mode;
- int i;
if (dev->mode_config.dithering_mode_property)
return 0;
dithering_mode =
- drm_property_create(dev, DRM_MODE_PROP_ENUM, "dithering",
+ drm_property_create_enum(dev, 0, "dithering",
+ drm_dithering_mode_enum_list,
ARRAY_SIZE(drm_dithering_mode_enum_list));
- for (i = 0; i < ARRAY_SIZE(drm_dithering_mode_enum_list); i++)
- drm_property_add_enum(dithering_mode, i,
- drm_dithering_mode_enum_list[i].type,
- drm_dithering_mode_enum_list[i].name);
dev->mode_config.dithering_mode_property = dithering_mode;
return 0;
@@ -902,20 +894,15 @@ EXPORT_SYMBOL(drm_mode_create_dithering_property);
int drm_mode_create_dirty_info_property(struct drm_device *dev)
{
struct drm_property *dirty_info;
- int i;
if (dev->mode_config.dirty_info_property)
return 0;
dirty_info =
- drm_property_create(dev, DRM_MODE_PROP_ENUM |
- DRM_MODE_PROP_IMMUTABLE,
+ drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE,
"dirty",
+ drm_dirty_info_enum_list,
ARRAY_SIZE(drm_dirty_info_enum_list));
- for (i = 0; i < ARRAY_SIZE(drm_dirty_info_enum_list); i++)
- drm_property_add_enum(dirty_info, i,
- drm_dirty_info_enum_list[i].type,
- drm_dirty_info_enum_list[i].name);
dev->mode_config.dirty_info_property = dirty_info;
return 0;
@@ -999,6 +986,7 @@ int drm_mode_group_init_legacy_group(struct drm_device *dev,
return 0;
}
+EXPORT_SYMBOL(drm_mode_group_init_legacy_group);
/**
* drm_mode_config_cleanup - free up DRM mode_config info
@@ -1048,6 +1036,9 @@ void drm_mode_config_cleanup(struct drm_device *dev)
head) {
plane->funcs->destroy(plane);
}
+
+ idr_remove_all(&dev->mode_config.crtc_idr);
+ idr_destroy(&dev->mode_config.crtc_idr);
}
EXPORT_SYMBOL(drm_mode_config_cleanup);
@@ -1062,9 +1053,16 @@ EXPORT_SYMBOL(drm_mode_config_cleanup);
* Convert a drm_display_mode into a drm_mode_modeinfo structure to return to
* the user.
*/
-void drm_crtc_convert_to_umode(struct drm_mode_modeinfo *out,
- struct drm_display_mode *in)
+static void drm_crtc_convert_to_umode(struct drm_mode_modeinfo *out,
+ const struct drm_display_mode *in)
{
+ WARN(in->hdisplay > USHRT_MAX || in->hsync_start > USHRT_MAX ||
+ in->hsync_end > USHRT_MAX || in->htotal > USHRT_MAX ||
+ in->hskew > USHRT_MAX || in->vdisplay > USHRT_MAX ||
+ in->vsync_start > USHRT_MAX || in->vsync_end > USHRT_MAX ||
+ in->vtotal > USHRT_MAX || in->vscan > USHRT_MAX,
+ "timing values too large for mode info\n");
+
out->clock = in->clock;
out->hdisplay = in->hdisplay;
out->hsync_start = in->hsync_start;
@@ -1093,10 +1091,16 @@ void drm_crtc_convert_to_umode(struct drm_mode_modeinfo *out,
*
* Convert a drm_mode_modeinfo into a drm_display_mode structure to return to
* the caller.
+ *
+ * RETURNS:
+ * Zero on success, errno on failure.
*/
-void drm_crtc_convert_umode(struct drm_display_mode *out,
- struct drm_mode_modeinfo *in)
+static int drm_crtc_convert_umode(struct drm_display_mode *out,
+ const struct drm_mode_modeinfo *in)
{
+ if (in->clock > INT_MAX || in->vrefresh > INT_MAX)
+ return -ERANGE;
+
out->clock = in->clock;
out->hdisplay = in->hdisplay;
out->hsync_start = in->hsync_start;
@@ -1113,6 +1117,8 @@ void drm_crtc_convert_umode(struct drm_display_mode *out,
out->type = in->type;
strncpy(out->name, in->name, DRM_DISPLAY_MODE_LEN);
out->name[DRM_DISPLAY_MODE_LEN-1] = 0;
+
+ return 0;
}
/**
@@ -1311,7 +1317,7 @@ out:
* @arg: arg from ioctl
*
* LOCKING:
- * Caller? (FIXME)
+ * Takes mode config lock.
*
* Construct a CRTC configuration structure to return to the user.
*
@@ -1371,7 +1377,7 @@ out:
* @arg: arg from ioctl
*
* LOCKING:
- * Caller? (FIXME)
+ * Takes mode config lock.
*
* Construct a connector configuration structure to return to the user.
*
@@ -1553,6 +1559,9 @@ out:
* @data: ioctl data
* @file_priv: DRM file info
*
+ * LOCKING:
+ * Takes mode config lock.
+ *
* Return an plane count and set of IDs.
*/
int drm_mode_getplane_res(struct drm_device *dev, void *data,
@@ -1599,6 +1608,9 @@ out:
* @data: ioctl data
* @file_priv: DRM file info
*
+ * LOCKING:
+ * Takes mode config lock.
+ *
* Return plane info, including formats supported, gamma size, any
* current fb, etc.
*/
@@ -1664,6 +1676,9 @@ out:
* @data: ioctl data*
* @file_prive: DRM file info
*
+ * LOCKING:
+ * Takes mode config lock.
+ *
* Set plane info, including placement, fb, scaling, and other factors.
* Or pass a NULL fb to disable.
*/
@@ -1794,7 +1809,7 @@ out:
* @arg: arg from ioctl
*
* LOCKING:
- * Caller? (FIXME)
+ * Takes mode config lock.
*
* Build a new CRTC configuration based on user request.
*
@@ -1809,7 +1824,7 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
struct drm_mode_config *config = &dev->mode_config;
struct drm_mode_crtc *crtc_req = data;
struct drm_mode_object *obj;
- struct drm_crtc *crtc, *crtcfb;
+ struct drm_crtc *crtc;
struct drm_connector **connector_set = NULL, *connector;
struct drm_framebuffer *fb = NULL;
struct drm_display_mode *mode = NULL;
@@ -1821,6 +1836,10 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
if (!drm_core_check_feature(dev, DRIVER_MODESET))
return -EINVAL;
+ /* For some reason crtc x/y offsets are signed internally. */
+ if (crtc_req->x > INT_MAX || crtc_req->y > INT_MAX)
+ return -ERANGE;
+
mutex_lock(&dev->mode_config.mutex);
obj = drm_mode_object_find(dev, crtc_req->crtc_id,
DRM_MODE_OBJECT_CRTC);
@@ -1836,14 +1855,12 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
/* If we have a mode we need a framebuffer. */
/* If we pass -1, set the mode with the currently bound fb */
if (crtc_req->fb_id == -1) {
- list_for_each_entry(crtcfb,
- &dev->mode_config.crtc_list, head) {
- if (crtcfb == crtc) {
- DRM_DEBUG_KMS("Using current fb for "
- "setmode\n");
- fb = crtc->fb;
- }
+ if (!crtc->fb) {
+ DRM_DEBUG_KMS("CRTC doesn't have current FB\n");
+ ret = -EINVAL;
+ goto out;
}
+ fb = crtc->fb;
} else {
obj = drm_mode_object_find(dev, crtc_req->fb_id,
DRM_MODE_OBJECT_FB);
@@ -1857,8 +1874,30 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
}
mode = drm_mode_create(dev);
- drm_crtc_convert_umode(mode, &crtc_req->mode);
+ if (!mode) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ret = drm_crtc_convert_umode(mode, &crtc_req->mode);
+ if (ret) {
+ DRM_DEBUG_KMS("Invalid mode\n");
+ goto out;
+ }
+
drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
+
+ if (mode->hdisplay > fb->width ||
+ mode->vdisplay > fb->height ||
+ crtc_req->x > fb->width - mode->hdisplay ||
+ crtc_req->y > fb->height - mode->vdisplay) {
+ DRM_DEBUG_KMS("Invalid CRTC viewport %ux%u+%u+%u for fb size %ux%u.\n",
+ mode->hdisplay, mode->vdisplay,
+ crtc_req->x, crtc_req->y,
+ fb->width, fb->height);
+ ret = -ENOSPC;
+ goto out;
+ }
}
if (crtc_req->count_connectors == 0 && mode) {
@@ -1926,6 +1965,7 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
out:
kfree(connector_set);
+ drm_mode_destroy(dev, mode);
mutex_unlock(&dev->mode_config.mutex);
return ret;
}
@@ -2275,7 +2315,7 @@ out:
* @arg: arg from ioctl
*
* LOCKING:
- * Caller? (FIXME)
+ * Takes mode config lock.
*
* Lookup the FB given its ID and return info about it.
*
@@ -2424,38 +2464,48 @@ void drm_fb_release(struct drm_file *priv)
*
* Add @mode to @connector's user mode list.
*/
-static int drm_mode_attachmode(struct drm_device *dev,
- struct drm_connector *connector,
- struct drm_display_mode *mode)
+static void drm_mode_attachmode(struct drm_device *dev,
+ struct drm_connector *connector,
+ struct drm_display_mode *mode)
{
- int ret = 0;
-
list_add_tail(&mode->head, &connector->user_modes);
- return ret;
}
int drm_mode_attachmode_crtc(struct drm_device *dev, struct drm_crtc *crtc,
- struct drm_display_mode *mode)
+ const struct drm_display_mode *mode)
{
struct drm_connector *connector;
int ret = 0;
- struct drm_display_mode *dup_mode;
- int need_dup = 0;
+ struct drm_display_mode *dup_mode, *next;
+ LIST_HEAD(list);
+
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
if (!connector->encoder)
- break;
+ continue;
if (connector->encoder->crtc == crtc) {
- if (need_dup)
- dup_mode = drm_mode_duplicate(dev, mode);
- else
- dup_mode = mode;
- ret = drm_mode_attachmode(dev, connector, dup_mode);
- if (ret)
- return ret;
- need_dup = 1;
+ dup_mode = drm_mode_duplicate(dev, mode);
+ if (!dup_mode) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ list_add_tail(&dup_mode->head, &list);
}
}
- return 0;
+
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ if (!connector->encoder)
+ continue;
+ if (connector->encoder->crtc == crtc)
+ list_move_tail(list.next, &connector->user_modes);
+ }
+
+ WARN_ON(!list_empty(&list));
+
+ out:
+ list_for_each_entry_safe(dup_mode, next, &list, head)
+ drm_mode_destroy(dev, dup_mode);
+
+ return ret;
}
EXPORT_SYMBOL(drm_mode_attachmode_crtc);
@@ -2534,9 +2584,14 @@ int drm_mode_attachmode_ioctl(struct drm_device *dev,
goto out;
}
- drm_crtc_convert_umode(mode, umode);
+ ret = drm_crtc_convert_umode(mode, umode);
+ if (ret) {
+ DRM_DEBUG_KMS("Invalid mode\n");
+ drm_mode_destroy(dev, mode);
+ goto out;
+ }
- ret = drm_mode_attachmode(dev, connector, mode);
+ drm_mode_attachmode(dev, connector, mode);
out:
mutex_unlock(&dev->mode_config.mutex);
return ret;
@@ -2577,7 +2632,12 @@ int drm_mode_detachmode_ioctl(struct drm_device *dev,
}
connector = obj_to_connector(obj);
- drm_crtc_convert_umode(&mode, umode);
+ ret = drm_crtc_convert_umode(&mode, umode);
+ if (ret) {
+ DRM_DEBUG_KMS("Invalid mode\n");
+ goto out;
+ }
+
ret = drm_mode_detachmode(dev, connector, &mode);
out:
mutex_unlock(&dev->mode_config.mutex);
@@ -2588,6 +2648,7 @@ struct drm_property *drm_property_create(struct drm_device *dev, int flags,
const char *name, int num_values)
{
struct drm_property *property = NULL;
+ int ret;
property = kzalloc(sizeof(struct drm_property), GFP_KERNEL);
if (!property)
@@ -2599,7 +2660,10 @@ struct drm_property *drm_property_create(struct drm_device *dev, int flags,
goto fail;
}
- drm_mode_object_get(dev, &property->base, DRM_MODE_OBJECT_PROPERTY);
+ ret = drm_mode_object_get(dev, &property->base, DRM_MODE_OBJECT_PROPERTY);
+ if (ret)
+ goto fail;
+
property->flags = flags;
property->num_values = num_values;
INIT_LIST_HEAD(&property->enum_blob_list);
@@ -2612,11 +2676,59 @@ struct drm_property *drm_property_create(struct drm_device *dev, int flags,
list_add_tail(&property->head, &dev->mode_config.property_list);
return property;
fail:
+ kfree(property->values);
kfree(property);
return NULL;
}
EXPORT_SYMBOL(drm_property_create);
+struct drm_property *drm_property_create_enum(struct drm_device *dev, int flags,
+ const char *name,
+ const struct drm_prop_enum_list *props,
+ int num_values)
+{
+ struct drm_property *property;
+ int i, ret;
+
+ flags |= DRM_MODE_PROP_ENUM;
+
+ property = drm_property_create(dev, flags, name, num_values);
+ if (!property)
+ return NULL;
+
+ for (i = 0; i < num_values; i++) {
+ ret = drm_property_add_enum(property, i,
+ props[i].type,
+ props[i].name);
+ if (ret) {
+ drm_property_destroy(dev, property);
+ return NULL;
+ }
+ }
+
+ return property;
+}
+EXPORT_SYMBOL(drm_property_create_enum);
+
+struct drm_property *drm_property_create_range(struct drm_device *dev, int flags,
+ const char *name,
+ uint64_t min, uint64_t max)
+{
+ struct drm_property *property;
+
+ flags |= DRM_MODE_PROP_RANGE;
+
+ property = drm_property_create(dev, flags, name, 2);
+ if (!property)
+ return NULL;
+
+ property->values[0] = min;
+ property->values[1] = max;
+
+ return property;
+}
+EXPORT_SYMBOL(drm_property_create_range);
+
int drm_property_add_enum(struct drm_property *property, int index,
uint64_t value, const char *name)
{
@@ -2828,6 +2940,7 @@ static struct drm_property_blob *drm_property_create_blob(struct drm_device *dev
void *data)
{
struct drm_property_blob *blob;
+ int ret;
if (!length || !data)
return NULL;
@@ -2836,13 +2949,16 @@ static struct drm_property_blob *drm_property_create_blob(struct drm_device *dev
if (!blob)
return NULL;
- blob->data = (void *)((char *)blob + sizeof(struct drm_property_blob));
+ ret = drm_mode_object_get(dev, &blob->base, DRM_MODE_OBJECT_BLOB);
+ if (ret) {
+ kfree(blob);
+ return NULL;
+ }
+
blob->length = length;
memcpy(blob->data, data, length);
- drm_mode_object_get(dev, &blob->base, DRM_MODE_OBJECT_BLOB);
-
list_add_tail(&blob->head, &dev->mode_config.property_blob_list);
return blob;
}
@@ -3021,7 +3137,7 @@ void drm_mode_connector_detach_encoder(struct drm_connector *connector,
}
EXPORT_SYMBOL(drm_mode_connector_detach_encoder);
-bool drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc,
+int drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc,
int gamma_size)
{
crtc->gamma_size = gamma_size;
@@ -3029,10 +3145,10 @@ bool drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc,
crtc->gamma_store = kzalloc(gamma_size * sizeof(uint16_t) * 3, GFP_KERNEL);
if (!crtc->gamma_store) {
crtc->gamma_size = 0;
- return false;
+ return -ENOMEM;
}
- return true;
+ return 0;
}
EXPORT_SYMBOL(drm_mode_crtc_set_gamma_size);
@@ -3178,6 +3294,18 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
goto out;
fb = obj_to_fb(obj);
+ if (crtc->mode.hdisplay > fb->width ||
+ crtc->mode.vdisplay > fb->height ||
+ crtc->x > fb->width - crtc->mode.hdisplay ||
+ crtc->y > fb->height - crtc->mode.vdisplay) {
+ DRM_DEBUG_KMS("Invalid fb size %ux%u for CRTC viewport %ux%u+%d+%d.\n",
+ fb->width, fb->height,
+ crtc->mode.hdisplay, crtc->mode.vdisplay,
+ crtc->x, crtc->y);
+ ret = -ENOSPC;
+ goto out;
+ }
+
if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) {
ret = -ENOMEM;
spin_lock_irqsave(&dev->event_lock, flags);