diff options
Diffstat (limited to 'drivers/gpu/drm/drm_crtc.c')
| -rw-r--r-- | drivers/gpu/drm/drm_crtc.c | 398 |
1 files changed, 322 insertions, 76 deletions
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 4936e1080e41..a7797d260f1e 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -35,9 +35,12 @@ #include <linux/export.h> #include <linux/dma-fence.h> #include <linux/uaccess.h> +#include <drm/drm_blend.h> #include <drm/drm_crtc.h> #include <drm/drm_edid.h> #include <drm/drm_fourcc.h> +#include <drm/drm_framebuffer.h> +#include <drm/drm_managed.h> #include <drm/drm_modeset_lock.h> #include <drm/drm_atomic.h> #include <drm/drm_auth.h> @@ -58,16 +61,16 @@ * to one or more &drm_encoder, which are then each connected to one * &drm_connector. * - * To create a CRTC, a KMS drivers allocates and zeroes an instances of + * To create a CRTC, a KMS driver allocates and zeroes an instance of * &struct drm_crtc (possibly as part of a larger structure) and registers it * with a call to drm_crtc_init_with_planes(). * - * The CRTC is also the entry point for legacy modeset operations, see - * &drm_crtc_funcs.set_config, legacy plane operations, see - * &drm_crtc_funcs.page_flip and &drm_crtc_funcs.cursor_set2, and other legacy + * The CRTC is also the entry point for legacy modeset operations (see + * &drm_crtc_funcs.set_config), legacy plane operations (see + * &drm_crtc_funcs.page_flip and &drm_crtc_funcs.cursor_set2), and other legacy * operations like &drm_crtc_funcs.gamma_set. For atomic drivers all these * features are controlled through &drm_property and - * &drm_mode_config_funcs.atomic_check and &drm_mode_config_funcs.atomic_check. + * &drm_mode_config_funcs.atomic_check. */ /** @@ -104,18 +107,6 @@ int drm_crtc_force_disable(struct drm_crtc *crtc) return drm_mode_set_config_internal(&set); } -static unsigned int drm_num_crtcs(struct drm_device *dev) -{ - unsigned int num = 0; - struct drm_crtc *tmp; - - drm_for_each_crtc(tmp, dev) { - num++; - } - - return num; -} - int drm_crtc_register_all(struct drm_device *dev) { struct drm_crtc *crtc; @@ -205,29 +196,66 @@ struct dma_fence *drm_crtc_create_fence(struct drm_crtc *crtc) } /** - * drm_crtc_init_with_planes - Initialise a new CRTC object with - * specified primary and cursor planes. - * @dev: DRM device - * @crtc: CRTC object to init - * @primary: Primary plane for CRTC - * @cursor: Cursor plane for CRTC - * @funcs: callbacks for the new CRTC - * @name: printf style format string for the CRTC name, or NULL for default name + * DOC: standard CRTC properties * - * Inits a new object created as base part of a driver crtc object. Drivers - * should use this function instead of drm_crtc_init(), which is only provided - * for backwards compatibility with drivers which do not yet support universal - * planes). For really simple hardware which has only 1 plane look at - * drm_simple_display_pipe_init() instead. + * DRM CRTCs have a few standardized properties: * - * Returns: - * Zero on success, error code on failure. + * ACTIVE: + * Atomic property for setting the power state of the CRTC. When set to 1 + * the CRTC will actively display content. When set to 0 the CRTC will be + * powered off. There is no expectation that user-space will reset CRTC + * resources like the mode and planes when setting ACTIVE to 0. + * + * User-space can rely on an ACTIVE change to 1 to never fail an atomic + * test as long as no other property has changed. If a change to ACTIVE + * fails an atomic test, this is a driver bug. For this reason setting + * ACTIVE to 0 must not release internal resources (like reserved memory + * bandwidth or clock generators). + * + * Note that the legacy DPMS property on connectors is internally routed + * to control this property for atomic drivers. + * MODE_ID: + * Atomic property for setting the CRTC display timings. The value is the + * ID of a blob containing the DRM mode info. To disable the CRTC, + * user-space must set this property to 0. + * + * Setting MODE_ID to 0 will release reserved resources for the CRTC. + * SCALING_FILTER: + * Atomic property for setting the scaling filter for CRTC scaler + * + * The value of this property can be one of the following: + * + * Default: + * Driver's default scaling filter + * Nearest Neighbor: + * Nearest Neighbor scaling filter + * SHARPNESS_STRENGTH: + * Atomic property for setting the sharpness strength/intensity by userspace. + * + * The value of this property is set as an integer value ranging + * from 0 - 255 where: + * + * 0: Sharpness feature is disabled(default value). + * + * 1: Minimum sharpness. + * + * 255: Maximum sharpness. + * + * User can gradually increase or decrease the sharpness level and can + * set the optimum value depending on content. + * This value will be passed to kernel through the UAPI. + * The setting of this property does not require modeset. + * The sharpness effect takes place post blending on the final composed output. + * If the feature is disabled, the content remains same without any sharpening effect + * and when this feature is applied, it enhances the clarity of the content. */ -int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc, - struct drm_plane *primary, - struct drm_plane *cursor, - const struct drm_crtc_funcs *funcs, - const char *name, ...) + +__printf(6, 0) +static int __drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc, + struct drm_plane *primary, + struct drm_plane *cursor, + const struct drm_crtc_funcs *funcs, + const char *name, va_list ap) { struct drm_mode_config *config = &dev->mode_config; int ret; @@ -255,14 +283,9 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc, return ret; if (name) { - va_list ap; - - va_start(ap, name); crtc->name = kvasprintf(GFP_KERNEL, name, ap); - va_end(ap); } else { - crtc->name = kasprintf(GFP_KERNEL, "crtc-%d", - drm_num_crtcs(dev)); + crtc->name = kasprintf(GFP_KERNEL, "crtc-%d", config->num_crtc); } if (!crtc->name) { drm_mode_object_unregister(dev, &crtc->base); @@ -303,8 +326,171 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc, return 0; } + +/** + * drm_crtc_init_with_planes - Initialise a new CRTC object with + * specified primary and cursor planes. + * @dev: DRM device + * @crtc: CRTC object to init + * @primary: Primary plane for CRTC + * @cursor: Cursor plane for CRTC + * @funcs: callbacks for the new CRTC + * @name: printf style format string for the CRTC name, or NULL for default name + * + * Inits a new object created as base part of a driver crtc object. Drivers + * should use this function instead of drm_crtc_init(), which is only provided + * for backwards compatibility with drivers which do not yet support universal + * planes). For really simple hardware which has only 1 plane look at + * drm_simple_display_pipe_init() instead. + * The &drm_crtc_funcs.destroy hook should call drm_crtc_cleanup() and kfree() + * the crtc structure. The crtc structure should not be allocated with + * devm_kzalloc(). + * + * The @primary and @cursor planes are only relevant for legacy uAPI, see + * &drm_crtc.primary and &drm_crtc.cursor. + * + * Note: consider using drmm_crtc_alloc_with_planes() or + * drmm_crtc_init_with_planes() instead of drm_crtc_init_with_planes() + * to let the DRM managed resource infrastructure take care of cleanup + * and deallocation. + * + * Returns: + * Zero on success, error code on failure. + */ +int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc, + struct drm_plane *primary, + struct drm_plane *cursor, + const struct drm_crtc_funcs *funcs, + const char *name, ...) +{ + va_list ap; + int ret; + + WARN_ON(!funcs->destroy); + + va_start(ap, name); + ret = __drm_crtc_init_with_planes(dev, crtc, primary, cursor, funcs, + name, ap); + va_end(ap); + + return ret; +} EXPORT_SYMBOL(drm_crtc_init_with_planes); +static void drmm_crtc_init_with_planes_cleanup(struct drm_device *dev, + void *ptr) +{ + struct drm_crtc *crtc = ptr; + + drm_crtc_cleanup(crtc); +} + +__printf(6, 0) +static int __drmm_crtc_init_with_planes(struct drm_device *dev, + struct drm_crtc *crtc, + struct drm_plane *primary, + struct drm_plane *cursor, + const struct drm_crtc_funcs *funcs, + const char *name, + va_list args) +{ + int ret; + + drm_WARN_ON(dev, funcs && funcs->destroy); + + ret = __drm_crtc_init_with_planes(dev, crtc, primary, cursor, funcs, + name, args); + if (ret) + return ret; + + ret = drmm_add_action_or_reset(dev, drmm_crtc_init_with_planes_cleanup, + crtc); + if (ret) + return ret; + + return 0; +} + +/** + * drmm_crtc_init_with_planes - Initialise a new CRTC object with + * specified primary and cursor planes. + * @dev: DRM device + * @crtc: CRTC object to init + * @primary: Primary plane for CRTC + * @cursor: Cursor plane for CRTC + * @funcs: callbacks for the new CRTC + * @name: printf style format string for the CRTC name, or NULL for default name + * + * Inits a new object created as base part of a driver crtc object. Drivers + * should use this function instead of drm_crtc_init(), which is only provided + * for backwards compatibility with drivers which do not yet support universal + * planes). For really simple hardware which has only 1 plane look at + * drm_simple_display_pipe_init() instead. + * + * Cleanup is automatically handled through registering + * drmm_crtc_cleanup() with drmm_add_action(). The crtc structure should + * be allocated with drmm_kzalloc(). + * + * The @drm_crtc_funcs.destroy hook must be NULL. + * + * The @primary and @cursor planes are only relevant for legacy uAPI, see + * &drm_crtc.primary and &drm_crtc.cursor. + * + * Returns: + * Zero on success, error code on failure. + */ +int drmm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc, + struct drm_plane *primary, + struct drm_plane *cursor, + const struct drm_crtc_funcs *funcs, + const char *name, ...) +{ + va_list ap; + int ret; + + va_start(ap, name); + ret = __drmm_crtc_init_with_planes(dev, crtc, primary, cursor, funcs, + name, ap); + va_end(ap); + if (ret) + return ret; + + return 0; +} +EXPORT_SYMBOL(drmm_crtc_init_with_planes); + +void *__drmm_crtc_alloc_with_planes(struct drm_device *dev, + size_t size, size_t offset, + struct drm_plane *primary, + struct drm_plane *cursor, + const struct drm_crtc_funcs *funcs, + const char *name, ...) +{ + void *container; + struct drm_crtc *crtc; + va_list ap; + int ret; + + if (WARN_ON(!funcs || funcs->destroy)) + return ERR_PTR(-EINVAL); + + container = drmm_kzalloc(dev, size, GFP_KERNEL); + if (!container) + return ERR_PTR(-ENOMEM); + + crtc = container + offset; + + va_start(ap, name); + ret = __drmm_crtc_init_with_planes(dev, crtc, primary, cursor, funcs, + name, ap); + va_end(ap); + if (ret) + return ERR_PTR(ret); + + return container; +} +EXPORT_SYMBOL(__drmm_crtc_alloc_with_planes); + /** * drm_crtc_cleanup - Clean up the core crtc usage * @crtc: CRTC to cleanup @@ -535,8 +721,7 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, struct drm_mode_set set; uint32_t __user *set_connectors_ptr; struct drm_modeset_acquire_ctx ctx; - int ret; - int i; + int ret, i, num_connectors = 0; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EOPNOTSUPP; @@ -550,10 +735,10 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, crtc = drm_crtc_find(dev, file_priv, crtc_req->crtc_id); if (!crtc) { - DRM_DEBUG_KMS("Unknown CRTC ID %d\n", crtc_req->crtc_id); + drm_dbg_kms(dev, "Unknown CRTC ID %d\n", crtc_req->crtc_id); return -ENOENT; } - DRM_DEBUG_KMS("[CRTC:%d:%s]\n", crtc->base.id, crtc->name); + drm_dbg_kms(dev, "[CRTC:%d:%s]\n", crtc->base.id, crtc->name); plane = crtc->primary; @@ -561,7 +746,6 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, if (crtc_req->mode_valid && !drm_lease_held(file_priv, plane->base.id)) return -EACCES; - mutex_lock(&crtc->dev->mode_config.mutex); DRM_MODESET_LOCK_ALL_BEGIN(dev, ctx, DRM_MODESET_ACQUIRE_INTERRUPTIBLE, ret); @@ -577,7 +761,7 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, old_fb = plane->fb; if (!old_fb) { - DRM_DEBUG_KMS("CRTC doesn't have current FB\n"); + drm_dbg_kms(dev, "CRTC doesn't have current FB\n"); ret = -EINVAL; goto out; } @@ -588,8 +772,8 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, } else { fb = drm_framebuffer_lookup(dev, file_priv, crtc_req->fb_id); if (!fb) { - DRM_DEBUG_KMS("Unknown FB ID%d\n", - crtc_req->fb_id); + drm_dbg_kms(dev, "Unknown FB ID%d\n", + crtc_req->fb_id); ret = -ENOENT; goto out; } @@ -602,7 +786,7 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, } if (!file_priv->aspect_ratio_allowed && (crtc_req->mode.flags & DRM_MODE_FLAG_PIC_AR_MASK) != DRM_MODE_FLAG_PIC_AR_NONE) { - DRM_DEBUG_KMS("Unexpected aspect-ratio flag bits\n"); + drm_dbg_kms(dev, "Unexpected aspect-ratio flag bits\n"); ret = -EINVAL; goto out; } @@ -610,9 +794,9 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, ret = drm_mode_convert_umode(dev, mode, &crtc_req->mode); if (ret) { - DRM_DEBUG_KMS("Invalid mode (ret=%d, status=%s)\n", - ret, drm_get_mode_status_name(mode->status)); - drm_mode_debug_printmodeline(mode); + drm_dbg_kms(dev, "Invalid mode (%s, %pe): " DRM_MODE_FMT "\n", + drm_get_mode_status_name(mode->status), + ERR_PTR(ret), DRM_MODE_ARG(mode)); goto out; } @@ -624,15 +808,10 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, * case. */ if (!plane->format_default) { - ret = drm_plane_check_pixel_format(plane, - fb->format->format, - fb->modifier); - if (ret) { - struct drm_format_name_buf format_name; - DRM_DEBUG_KMS("Invalid pixel format %s, modifier 0x%llx\n", - drm_get_format_name(fb->format->format, - &format_name), - fb->modifier); + if (!drm_plane_has_format(plane, fb->format->format, fb->modifier)) { + drm_dbg_kms(dev, "Invalid pixel format %p4cc, modifier 0x%llx\n", + &fb->format->format, fb->modifier); + ret = -EINVAL; goto out; } } @@ -645,14 +824,14 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, } if (crtc_req->count_connectors == 0 && mode) { - DRM_DEBUG_KMS("Count connectors is 0 but mode set\n"); + drm_dbg_kms(dev, "Count connectors is 0 but mode set\n"); ret = -EINVAL; goto out; } if (crtc_req->count_connectors > 0 && (!mode || !fb)) { - DRM_DEBUG_KMS("Count connectors is %d but no mode or fb set\n", - crtc_req->count_connectors); + drm_dbg_kms(dev, "Count connectors is %d but no mode or fb set\n", + crtc_req->count_connectors); ret = -EINVAL; goto out; } @@ -684,16 +863,16 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, connector = drm_connector_lookup(dev, file_priv, out_id); if (!connector) { - DRM_DEBUG_KMS("Connector id %d unknown\n", - out_id); + drm_dbg_kms(dev, "Connector id %d unknown\n", + out_id); ret = -ENOENT; goto out; } - DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", - connector->base.id, - connector->name); + drm_dbg_kms(dev, "[CONNECTOR:%d:%s]\n", + connector->base.id, connector->name); connector_set[i] = connector; + num_connectors++; } } @@ -702,7 +881,7 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, set.y = crtc_req->y; set.mode = mode; set.connectors = connector_set; - set.num_connectors = crtc_req->count_connectors; + set.num_connectors = num_connectors; set.fb = fb; if (drm_drv_uses_atomic_modeset(dev)) @@ -715,7 +894,7 @@ out: drm_framebuffer_put(fb); if (connector_set) { - for (i = 0; i < crtc_req->count_connectors; i++) { + for (i = 0; i < num_connectors; i++) { if (connector_set[i]) drm_connector_put(connector_set[i]); } @@ -727,9 +906,9 @@ out: connector_set = NULL; fb = NULL; mode = NULL; + num_connectors = 0; - DRM_MODESET_LOCK_ALL_END(ctx, ret); - mutex_unlock(&crtc->dev->mode_config.mutex); + DRM_MODESET_LOCK_ALL_END(dev, ctx, ret); return ret; } @@ -748,3 +927,70 @@ int drm_mode_crtc_set_obj_prop(struct drm_mode_object *obj, return ret; } + +/** + * drm_crtc_create_scaling_filter_property - create a new scaling filter + * property + * + * @crtc: drm CRTC + * @supported_filters: bitmask of supported scaling filters, must include + * BIT(DRM_SCALING_FILTER_DEFAULT). + * + * This function lets driver to enable the scaling filter property on a given + * CRTC. + * + * RETURNS: + * Zero for success or -errno + */ +int drm_crtc_create_scaling_filter_property(struct drm_crtc *crtc, + unsigned int supported_filters) +{ + struct drm_property *prop = + drm_create_scaling_filter_prop(crtc->dev, supported_filters); + + if (IS_ERR(prop)) + return PTR_ERR(prop); + + drm_object_attach_property(&crtc->base, prop, + DRM_SCALING_FILTER_DEFAULT); + crtc->scaling_filter_property = prop; + + return 0; +} +EXPORT_SYMBOL(drm_crtc_create_scaling_filter_property); + +int drm_crtc_create_sharpness_strength_property(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct drm_property *prop = + drm_property_create_range(dev, 0, "SHARPNESS_STRENGTH", 0, 255); + + if (!prop) + return -ENOMEM; + + crtc->sharpness_strength_property = prop; + drm_object_attach_property(&crtc->base, prop, 0); + + return 0; +} +EXPORT_SYMBOL(drm_crtc_create_sharpness_strength_property); + +/** + * drm_crtc_in_clone_mode - check if the given CRTC state is in clone mode + * + * @crtc_state: CRTC state to check + * + * This function determines if the given CRTC state is being cloned by multiple + * encoders. + * + * RETURNS: + * True if the CRTC state is in clone mode. False otherwise + */ +bool drm_crtc_in_clone_mode(struct drm_crtc_state *crtc_state) +{ + if (!crtc_state) + return false; + + return hweight32(crtc_state->encoder_mask) > 1; +} +EXPORT_SYMBOL(drm_crtc_in_clone_mode); |
