{ DRM_MODE_CONNECTOR_DSI, "DSI" },
};
+DRM_ENUM_NAME_FN(drm_get_connector_name, drm_connector_enum_list)
+EXPORT_SYMBOL(drm_get_connector_name);
+
static const struct drm_prop_enum_list drm_encoder_enum_list[] = {
{ DRM_MODE_ENCODER_NONE, "None" },
{ DRM_MODE_ENCODER_DAC, "DAC" },
DEFINE_WW_CLASS(crtc_ww_class);
+static int drm_crtc_register_all(struct drm_device *dev)
+{
+ struct drm_crtc *crtc;
+ int ret = 0;
+
+ drm_for_each_crtc(crtc, dev) {
+ if (crtc->funcs->late_register)
+ ret = crtc->funcs->late_register(crtc);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static void drm_crtc_unregister_all(struct drm_device *dev)
+{
+ struct drm_crtc *crtc;
+
+ drm_for_each_crtc(crtc, dev) {
+ if (crtc->funcs->early_unregister)
+ crtc->funcs->early_unregister(crtc);
+ }
+}
+
/**
* drm_crtc_init_with_planes - Initialise a new CRTC object with
* specified primary and cursor planes.
* @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.
*
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 struct drm_crtc_funcs *funcs,
+ const char *name, ...)
{
struct drm_mode_config *config = &dev->mode_config;
int ret;
ret = drm_debugfs_connector_add(connector);
if (ret) {
- drm_sysfs_connector_remove(connector);
- return ret;
+ goto err_sysfs;
+ }
+
+ if (connector->funcs->late_register) {
+ ret = connector->funcs->late_register(connector);
+ if (ret)
+ goto err_debugfs;
}
return 0;
+
+err_debugfs:
+ drm_debugfs_connector_remove(connector);
+err_sysfs:
+ drm_sysfs_connector_remove(connector);
+ return ret;
}
EXPORT_SYMBOL(drm_connector_register);
*/
void drm_connector_unregister(struct drm_connector *connector)
{
+ if (connector->funcs->early_unregister)
+ connector->funcs->early_unregister(connector);
+
drm_sysfs_connector_remove(connector);
drm_debugfs_connector_remove(connector);
}
EXPORT_SYMBOL(drm_connector_unregister);
+/**
+ * drm_connector_register_all - register all connectors
+ * @dev: drm device
+ *
+ * This function registers all connectors in sysfs and other places so that
+ * userspace can start to access them. drm_connector_register_all() is called
+ * automatically from drm_dev_register() to complete the device registration,
+ * if they don't call drm_connector_register() on each connector individually.
+ *
+ * When a device is unplugged and should be removed from userspace access,
+ * call drm_connector_unregister_all(), which is the inverse of this
+ * function.
+ *
+ * Returns:
+ * Zero on success, error code on failure.
+ */
+int drm_connector_register_all(struct drm_device *dev)
+{
+ struct drm_connector *connector;
+ int ret;
+
+ /* FIXME: taking the mode config mutex ends up in a clash with
+ * fbcon/backlight registration */
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ ret = drm_connector_register(connector);
+ if (ret)
+ goto err;
+ }
+
+ return 0;
+
+err:
+ mutex_unlock(&dev->mode_config.mutex);
+ drm_connector_unregister_all(dev);
+ return ret;
+}
+EXPORT_SYMBOL(drm_connector_register_all);
/**
- * drm_connector_unplug_all - unregister connector userspace interfaces
+ * drm_connector_unregister_all - unregister connector userspace interfaces
* @dev: drm device
*
- * This function unregisters all connector userspace interfaces in sysfs. Should
- * be call when the device is disconnected, e.g. from an usb driver's
- * ->disconnect callback.
+ * This functions unregisters all connectors from sysfs and other places so
+ * that userspace can no longer access them. Drivers should call this as the
+ * first step tearing down the device instace, or when the underlying
+ * physical device disappeared (e.g. USB unplug), right before calling
+ * drm_dev_unregister().
*/
-void drm_connector_unplug_all(struct drm_device *dev)
+void drm_connector_unregister_all(struct drm_device *dev)
{
struct drm_connector *connector;
/* FIXME: taking the mode config mutex ends up in a clash with sysfs */
- list_for_each_entry(connector, &dev->mode_config.connector_list, head)
+ drm_for_each_connector(connector, dev)
drm_connector_unregister(connector);
+}
+EXPORT_SYMBOL(drm_connector_unregister_all);
+static int drm_encoder_register_all(struct drm_device *dev)
+{
+ struct drm_encoder *encoder;
+ int ret = 0;
+
+ drm_for_each_encoder(encoder, dev) {
+ if (encoder->funcs->late_register)
+ ret = encoder->funcs->late_register(encoder);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static void drm_encoder_unregister_all(struct drm_device *dev)
+{
+ struct drm_encoder *encoder;
+
+ drm_for_each_encoder(encoder, dev) {
+ if (encoder->funcs->early_unregister)
+ encoder->funcs->early_unregister(encoder);
+ }
}
-EXPORT_SYMBOL(drm_connector_unplug_all);
/**
* drm_encoder_init - Init a preallocated encoder
* @encoder: the encoder to init
* @funcs: callbacks for this encoder
* @encoder_type: user visible type of the encoder
+ * @name: printf style format string for the encoder name, or NULL for default name
*
* Initialises a preallocated encoder. Encoder should be
* subclassed as part of driver encoder objects.
int drm_encoder_init(struct drm_device *dev,
struct drm_encoder *encoder,
const struct drm_encoder_funcs *funcs,
- int encoder_type)
+ int encoder_type, const char *name, ...)
{
int ret;
* @formats: array of supported formats (%DRM_FORMAT_*)
* @format_count: number of elements in @formats
* @type: type of plane (overlay, primary, cursor)
+ * @name: printf style format string for the plane name, or NULL for default name
*
* Initializes a plane object of type @type.
*
unsigned long possible_crtcs,
const struct drm_plane_funcs *funcs,
const uint32_t *formats, unsigned int format_count,
- enum drm_plane_type type)
+ enum drm_plane_type type,
+ const char *name, ...)
{
struct drm_mode_config *config = &dev->mode_config;
int ret;
}
EXPORT_SYMBOL(drm_universal_plane_init);
+static int drm_plane_register_all(struct drm_device *dev)
+{
+ struct drm_plane *plane;
+ int ret = 0;
+
+ drm_for_each_plane(plane, dev) {
+ if (plane->funcs->late_register)
+ ret = plane->funcs->late_register(plane);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static void drm_plane_unregister_all(struct drm_device *dev)
+{
+ struct drm_plane *plane;
+
+ drm_for_each_plane(plane, dev) {
+ if (plane->funcs->early_unregister)
+ plane->funcs->early_unregister(plane);
+ }
+}
+
/**
* drm_plane_init - Initialize a legacy plane
* @dev: DRM device
type = is_primary ? DRM_PLANE_TYPE_PRIMARY : DRM_PLANE_TYPE_OVERLAY;
return drm_universal_plane_init(dev, plane, possible_crtcs, funcs,
- formats, format_count, type);
+ formats, format_count, type, NULL);
}
EXPORT_SYMBOL(drm_plane_init);
+/**
+ * drm_share_plane_init - Initialize a share plane
+ * @dev: DRM device
+ * @plane: plane object to init
+ * @parent: this plane share some resources with parent plane.
+ * @possible_crtcs: bitmask of possible CRTCs
+ * @funcs: callbacks for the new plane
+ * @formats: array of supported formats (%DRM_FORMAT_*)
+ * @format_count: number of elements in @formats
+ * @type: type of plane (overlay, primary, cursor)
+ *
+ * With this API, the plane can share hardware resources with other planes.
+ *
+ * --------------------------------------------------
+ * | scanout |
+ * | ------------------ |
+ * | | parent plane | |
+ * | | active scanout | |
+ * | | | ----------------- |
+ * | ------------------ | share plane 1 | |
+ * | ----------------- |active scanout | |
+ * | | share plane 0 | | | |
+ * | |active scanout | ----------------- |
+ * | | | |
+ * | ----------------- |
+ * --------------------------------------------------
+ *
+ * parent plane
+ * |---share plane 0
+ * |---share plane 1
+ * ...
+ *
+ * The plane hardware is used when the display scanout run into plane active
+ * scanout, that means we can reuse the plane hardware resources on plane
+ * non-active scanout.
+ *
+ * Because resource share, There are some limit on share plane: one group
+ * of share planes need use same zpos, can't not overlap, etc.
+ *
+ * Here assume share plane is a universal plane with some limit flags.
+ * people who use the share plane need know the limit, should call the ioctl
+ * DRM_CLIENT_CAP_SHARE_PLANES, and judge the planes limit before use it.
+ *
+ * Returns:
+ * Zero on success, error code on failure.
+ */
+
+int drm_share_plane_init(struct drm_device *dev, struct drm_plane *plane,
+ struct drm_plane *parent,
+ unsigned long possible_crtcs,
+ const struct drm_plane_funcs *funcs,
+ const uint32_t *formats, unsigned int format_count,
+ enum drm_plane_type type)
+{
+ struct drm_mode_config *config = &dev->mode_config;
+ int ret;
+ int share_id;
+
+ /*
+ * TODO: only verified on ATOMIC drm driver.
+ */
+ if (!drm_core_check_feature(dev, DRIVER_ATOMIC))
+ return -EINVAL;
+
+ ret = drm_universal_plane_init(dev, plane, possible_crtcs, funcs,
+ formats, format_count, type, NULL);
+ if (ret)
+ return ret;
+
+ if (parent) {
+ /*
+ * Can't support more than two level plane share.
+ */
+ WARN_ON(parent->parent);
+ share_id = parent->base.id;
+ plane->parent = parent;
+
+ config->num_share_plane++;
+ if (plane->type == DRM_PLANE_TYPE_OVERLAY)
+ config->num_share_overlay_plane++;
+ } else {
+ share_id = plane->base.id;
+ }
+
+ drm_object_attach_property(&plane->base,
+ config->prop_share_id, share_id);
+ return 0;
+}
+EXPORT_SYMBOL(drm_share_plane_init);
+
/**
* drm_plane_cleanup - Clean up the core plane usage
* @plane: plane to cleanup
dev->mode_config.num_total_plane--;
if (plane->type == DRM_PLANE_TYPE_OVERLAY)
dev->mode_config.num_overlay_plane--;
+ if (plane->parent) {
+ dev->mode_config.num_share_plane--;
+ if (plane->type == DRM_PLANE_TYPE_OVERLAY)
+ dev->mode_config.num_share_overlay_plane--;
+ }
drm_modeset_unlock_all(dev);
WARN_ON(plane->state && !plane->funcs->atomic_destroy_state);
}
EXPORT_SYMBOL(drm_plane_force_disable);
+int drm_modeset_register_all(struct drm_device *dev)
+{
+ int ret;
+
+ ret = drm_plane_register_all(dev);
+ if (ret)
+ goto err_plane;
+
+ ret = drm_crtc_register_all(dev);
+ if (ret)
+ goto err_crtc;
+
+ ret = drm_encoder_register_all(dev);
+ if (ret)
+ goto err_encoder;
+
+ ret = drm_connector_register_all(dev);
+ if (ret)
+ goto err_connector;
+
+ return 0;
+
+err_connector:
+ drm_encoder_unregister_all(dev);
+err_encoder:
+ drm_crtc_unregister_all(dev);
+err_crtc:
+ drm_plane_unregister_all(dev);
+err_plane:
+ return ret;
+}
+
+void drm_modeset_unregister_all(struct drm_device *dev)
+{
+ drm_connector_unregister_all(dev);
+ drm_encoder_unregister_all(dev);
+ drm_crtc_unregister_all(dev);
+ drm_plane_unregister_all(dev);
+}
+
static int drm_mode_create_standard_properties(struct drm_device *dev)
{
struct drm_property *prop;
return -ENOMEM;
dev->mode_config.plane_type_property = prop;
+ prop = drm_property_create_range(dev, DRM_MODE_PROP_IMMUTABLE,
+ "SHARE_ID", 0, UINT_MAX);
+ if (!prop)
+ return -ENOMEM;
+
+ dev->mode_config.prop_share_id = prop;
+ prop = drm_property_create_range(dev, DRM_MODE_PROP_IMMUTABLE,
+ "SHARE_FLAGS", 0, UINT_MAX);
+ if (!prop)
+ return -ENOMEM;
+ dev->mode_config.prop_share_flags = prop;
+
prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
"SRC_X", 0, UINT_MAX);
if (!prop)
num_planes = config->num_total_plane;
else
num_planes = config->num_overlay_plane;
+ if (!file_priv->share_planes) {
+ if (file_priv->universal_planes)
+ num_planes -= config->num_share_plane;
+ else
+ num_planes -= config->num_share_overlay_plane;
+ }
/*
* This ioctl is called twice, once to determine how much space is
if (plane->type != DRM_PLANE_TYPE_OVERLAY &&
!file_priv->universal_planes)
continue;
+ if (plane->parent && !file_priv->share_planes)
+ continue;
if (put_user(plane->base.id, plane_ptr + copied))
return -EFAULT;
goto out;
}
- drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
-
/*
* Check whether the primary plane supports the fb pixel format.
* Drivers not implementing the universal planes API use a
case DRM_FORMAT_NV61:
case DRM_FORMAT_NV24:
case DRM_FORMAT_NV42:
+ case DRM_FORMAT_NV12_10:
+ case DRM_FORMAT_NV21_10:
+ case DRM_FORMAT_NV16_10:
+ case DRM_FORMAT_NV61_10:
+ case DRM_FORMAT_NV24_10:
+ case DRM_FORMAT_NV42_10:
case DRM_FORMAT_YUV410:
case DRM_FORMAT_YVU410:
case DRM_FORMAT_YUV411:
for (i = 0; i < num_planes; i++) {
unsigned int width = r->width / (i != 0 ? hsub : 1);
unsigned int height = r->height / (i != 0 ? vsub : 1);
- unsigned int cpp = drm_format_plane_cpp(r->pixel_format, i);
+ unsigned int bpp = drm_format_plane_bpp(r->pixel_format, i);
if (!r->handles[i]) {
DRM_DEBUG_KMS("no buffer object handle for plane %d\n", i);
return -EINVAL;
}
- if ((uint64_t) width * cpp > UINT_MAX)
+ if ((uint64_t) width * bpp / 8 > UINT_MAX)
return -ERANGE;
if ((uint64_t) height * r->pitches[i] + r->offsets[i] > UINT_MAX)
return -ERANGE;
- if (r->pitches[i] < width * cpp) {
+ if (r->pitches[i] < roundup(width * bpp, 8) / 8) {
DRM_DEBUG_KMS("bad pitch %u for plane %d\n", r->pitches[i], i);
return -EINVAL;
}
{
int i;
+ /*
+ * In the past, drivers have attempted to model the static association
+ * of connector to encoder in simple connector/encoder devices using a
+ * direct assignment of connector->encoder = encoder. This connection
+ * is a logical one and the responsibility of the core, so drivers are
+ * expected not to mess with this.
+ *
+ * Note that the error return should've been enough here, but a large
+ * majority of drivers ignores the return value, so add in a big WARN
+ * to get people's attention.
+ */
+ if (WARN_ON(connector->encoder))
+ return -EINVAL;
+
for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
if (connector->encoder_ids[i] == 0) {
connector->encoder_ids[i] = encoder->base.id;
unsigned long flags;
int ret = -EINVAL;
+ if (!drm_core_check_feature(dev, DRIVER_MODESET))
+ return -EINVAL;
+
if (page_flip->flags & ~DRM_MODE_PAGE_FLIP_FLAGS ||
page_flip->reserved != 0)
return -EINVAL;
case DRM_FORMAT_NV61:
case DRM_FORMAT_NV24:
case DRM_FORMAT_NV42:
+ case DRM_FORMAT_NV12_10:
+ case DRM_FORMAT_NV21_10:
+ case DRM_FORMAT_NV16_10:
+ case DRM_FORMAT_NV61_10:
+ case DRM_FORMAT_NV24_10:
+ case DRM_FORMAT_NV42_10:
return 2;
default:
return 1;
EXPORT_SYMBOL(drm_format_num_planes);
/**
- * drm_format_plane_cpp - determine the bytes per pixel value
+ * drm_format_plane_bpp - get the bpp for format
* @format: pixel format (DRM_FORMAT_*)
* @plane: plane index
*
* Returns:
- * The bytes per pixel value for the specified plane.
+ * The bpp for the specified plane.
*/
-int drm_format_plane_cpp(uint32_t format, int plane)
+int drm_format_plane_bpp(uint32_t format, int plane)
{
unsigned int depth;
int bpp;
case DRM_FORMAT_YVYU:
case DRM_FORMAT_UYVY:
case DRM_FORMAT_VYUY:
- return 2;
+ return 16;
+ case DRM_FORMAT_NV12_10:
+ case DRM_FORMAT_NV21_10:
+ case DRM_FORMAT_NV16_10:
+ case DRM_FORMAT_NV61_10:
+ case DRM_FORMAT_NV24_10:
+ case DRM_FORMAT_NV42_10:
+ return plane ? 20 : 10;
case DRM_FORMAT_NV12:
case DRM_FORMAT_NV21:
case DRM_FORMAT_NV16:
case DRM_FORMAT_NV61:
case DRM_FORMAT_NV24:
case DRM_FORMAT_NV42:
- return plane ? 2 : 1;
+ return plane ? 16 : 8;
case DRM_FORMAT_YUV410:
case DRM_FORMAT_YVU410:
case DRM_FORMAT_YUV411:
case DRM_FORMAT_YVU422:
case DRM_FORMAT_YUV444:
case DRM_FORMAT_YVU444:
- return 1;
+ return 8;
default:
drm_fb_get_bpp_depth(format, &depth, &bpp);
- return bpp >> 3;
+ return bpp;
}
}
+EXPORT_SYMBOL(drm_format_plane_bpp);
+
+/**
+ * drm_format_plane_cpp - determine the bytes per pixel value
+ * @format: pixel format (DRM_FORMAT_*)
+ * @plane: plane index
+ *
+ * Returns:
+ * The bytes per pixel value for the specified plane.
+ */
+int drm_format_plane_cpp(uint32_t format, int plane)
+{
+ return drm_format_plane_bpp(format, plane) >> 3;
+}
EXPORT_SYMBOL(drm_format_plane_cpp);
/**
case DRM_FORMAT_NV21:
case DRM_FORMAT_NV16:
case DRM_FORMAT_NV61:
+ case DRM_FORMAT_NV12_10:
+ case DRM_FORMAT_NV21_10:
+ case DRM_FORMAT_NV16_10:
+ case DRM_FORMAT_NV61_10:
case DRM_FORMAT_YUV422:
case DRM_FORMAT_YVU422:
case DRM_FORMAT_YUV420:
case DRM_FORMAT_YUV420:
case DRM_FORMAT_YVU420:
case DRM_FORMAT_NV12:
+ case DRM_FORMAT_NV12_10:
case DRM_FORMAT_NV21:
+ case DRM_FORMAT_NV21_10:
return 2;
default:
return 1;