diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-03-22 13:08:22 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-03-22 13:08:22 -0700 |
commit | be53bfdb8088e9d1924199cc1a96e113756b1075 (patch) | |
tree | 8c65eb9d82ca4c0f11c17cfdc44d5263820b415b /drivers/gpu/drm/drm_crtc.c | |
parent | b2094ef840697bc8ca5d17a83b7e30fad5f1e9fa (diff) | |
parent | 5466c7b1683a23dbbcfb7ee4a71c4f23886001c7 (diff) |
Merge branch 'drm-next' of git://people.freedesktop.org/~airlied/linux
Pull drm main changes from Dave Airlie:
"This is the main drm pull request, I'm probably going to send two more
smaller ones, will explain below.
This contains a patch that is also in the fbdev tree, but it should be
the same patch, it added an API for hot unplugging framebuffer
devices, and I need that API for a new driver.
It also contains some changes to the i2c tree which Jean has acked,
and one change to moorestown platform stuff in x86.
Highlights:
- new drivers: UDL driver for USB displaylink devices, kms only,
should support correct hotplug operations.
- core: i2c speedups + better hotplug support, EDID overriding via
firmware interface - allows user to load a firmware for a broken
monitor/kvm from userspace, it even has documentation for it.
- exynos: new HDMI audio + hdmi 1.4 + virtual output driver
- gma500: code cleanup
- radeon: cleanups, CS optimisations, streamout support and pageflip
fix
- nouveau: NVD9 displayport support + more reclocking work
- i915: re-enabling GMBUS, finish gpu patch (might help hibernation
who knows), missed irq fixes, stencil tiling fixes, interlaced
support, aliasesd PPGTT support for SNB/IVB, swizzling for SNB/IVB,
semaphore fixes
As well as the usual bunch of cleanups and fixes all over the place.
I've got two things I'd like to merge a bit later:
a) AMD support for all their new radeonhd 7000 series GPU and APUs.
AMD dropped this a bit late due to insane internal review
processes, (please AMD just follow Intel and let open source guys
ship stuff early) however I don't want to penalise people who own
this hardware (since its been on sale for 3-4 months and GPU hw
doesn't exactly have a lifetime in years) and consign them to
using closed drivers for longer than necessary. The changes are
well contained and just plug into the driver new gpu functionality
so they should be fairly regression proof. I just want to give
them a bit of a run on the hw AMD kindly sent me.
b) drm prime/dma-buf interface code. This is just infrastructure
code to expose the dma-buf stuff to drm drivers and to userspace.
I'm not planning on pushing any driver support in this cycle
(except maybe exynos), but I'd like to get the infrastructure code
in so for the next cycle I can start getting the driver support
into the individual drivers. We have started driver support for
i915, nouveau and udl along with I think exynos and omap in
staging. However this code relies on the dma-buf tree being
pulled into your tree first since it needs the latest interfaces
from that tree. I'll push to get that tree sent asap.
(oh and any warnings you see in i915 are gcc's fault from what anyone
can see)."
Fix up trivial conflicts in arch/x86/platform/mrst/mrst.c due to the new
msic_thermal_platform_data() thermal function being added next to the
tc35876x_platform_data() i2c device function..
* 'drm-next' of git://people.freedesktop.org/~airlied/linux: (326 commits)
drm/i915: use DDC_ADDR instead of hard-coding it
drm/radeon: use DDC_ADDR instead of hard-coding it
drm: remove unneeded redefinition of DDC_ADDR
drm/exynos: added virtual display driver.
drm: allow loading an EDID as firmware to override broken monitor
drm/exynos: enable hdmi audio feature
drm/exynos: add default pixel format for plane
drm/exynos: cleanup exynos_hdmi.h
drm/exynos: add is_local member in exynos_drm_subdrv struct
drm/exynos: add subdrv open/close functions
drm/exynos: remove module of exynos drm subdrv
drm/exynos: release pending pageflip events when closed
drm/exynos: added new funtion to get/put dma address.
drm/exynos: update gem and buffer framework.
drm/exynos: added mode_fixup feature and code clean.
drm/exynos: add HDMI version 1.4 support
drm/exynos: remove exynos_mixer.h
gma500: Fix mmap frambuffer
drm/radeon: Drop radeon_gem_object_(un)pin.
drm/radeon: Restrict offset for legacy display engine.
...
Diffstat (limited to 'drivers/gpu/drm/drm_crtc.c')
-rw-r--r-- | drivers/gpu/drm/drm_crtc.c | 448 |
1 files changed, 288 insertions, 160 deletions
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 5e818a808ac..d3aaeb6ae23 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); |