drm/panel: Change dlen from u16 to u8
[firefly-linux-kernel-4.4.55.git] / drivers / gpu / drm / drm_crtc.c
index 24c5434abd1c44e1040676220b84002fdce74ee9..8a93755de3acd0814050065b0088fd0c5096d3ed 100644 (file)
@@ -170,6 +170,9 @@ static struct drm_conn_prop_enum_list drm_connector_enum_list[] = {
        { 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" },
@@ -649,6 +652,31 @@ EXPORT_SYMBOL(drm_framebuffer_remove);
 
 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.
@@ -657,6 +685,7 @@ DEFINE_WW_CLASS(crtc_ww_class);
  * @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.
  *
@@ -666,7 +695,8 @@ DEFINE_WW_CLASS(crtc_ww_class);
 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;
@@ -1028,11 +1058,22 @@ int drm_connector_register(struct drm_connector *connector)
 
        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);
 
@@ -1044,30 +1085,96 @@ 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
@@ -1075,6 +1182,7 @@ EXPORT_SYMBOL(drm_connector_unplug_all);
  * @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.
@@ -1085,7 +1193,7 @@ 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 encoder_type, const char *name, ...)
 {
        int ret;
 
@@ -1150,6 +1258,7 @@ EXPORT_SYMBOL(drm_encoder_cleanup);
  * @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.
  *
@@ -1160,7 +1269,8 @@ int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane,
                             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;
@@ -1213,6 +1323,31 @@ int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane,
 }
 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
@@ -1240,10 +1375,100 @@ int drm_plane_init(struct drm_device *dev, struct drm_plane *plane,
 
        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
@@ -1266,6 +1491,11 @@ void drm_plane_cleanup(struct drm_plane *plane)
        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);
@@ -1353,6 +1583,46 @@ void drm_plane_force_disable(struct drm_plane *plane)
 }
 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;
@@ -1397,6 +1667,18 @@ static int drm_mode_create_standard_properties(struct drm_device *dev)
                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)
@@ -2203,6 +2485,12 @@ int drm_mode_getplane_res(struct drm_device *dev, void *data,
                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
@@ -2221,6 +2509,8 @@ int drm_mode_getplane_res(struct drm_device *dev, void *data,
                        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;
@@ -2682,8 +2972,6 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
                        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
@@ -3115,6 +3403,12 @@ static int format_check(const struct drm_mode_fb_cmd2 *r)
        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:
@@ -3161,20 +3455,20 @@ static int framebuffer_check(const struct drm_mode_fb_cmd2 *r)
        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;
                }
@@ -3316,6 +3610,24 @@ int drm_mode_addfb2(struct drm_device *dev,
        return 0;
 }
 
+struct drm_mode_rmfb_work {
+       struct work_struct work;
+       struct list_head fbs;
+};
+
+static void drm_mode_rmfb_work_fn(struct work_struct *w)
+{
+       struct drm_mode_rmfb_work *arg = container_of(w, typeof(*arg), work);
+
+       while (!list_empty(&arg->fbs)) {
+               struct drm_framebuffer *fb =
+                       list_first_entry(&arg->fbs, typeof(*fb), filp_head);
+
+               list_del_init(&fb->filp_head);
+               drm_framebuffer_remove(fb);
+       }
+}
+
 /**
  * drm_mode_rmfb - remove an FB from the configuration
  * @dev: drm device for the ioctl
@@ -3356,7 +3668,25 @@ int drm_mode_rmfb(struct drm_device *dev,
        mutex_unlock(&dev->mode_config.fb_lock);
        mutex_unlock(&file_priv->fbs_lock);
 
-       drm_framebuffer_unreference(fb);
+       /*
+        * we now own the reference that was stored in the fbs list
+        *
+        * drm_framebuffer_remove may fail with -EINTR on pending signals,
+        * so run this in a separate stack as there's no way to correctly
+        * handle this after the fb is already removed from the lookup table.
+        */
+       if (atomic_read(&fb->refcount.refcount) > 1) {
+               struct drm_mode_rmfb_work arg;
+
+               INIT_WORK_ONSTACK(&arg.work, drm_mode_rmfb_work_fn);
+               INIT_LIST_HEAD(&arg.fbs);
+               list_add_tail(&fb->filp_head, &arg.fbs);
+
+               schedule_work(&arg.work);
+               flush_work(&arg.work);
+               destroy_work_on_stack(&arg.work);
+       } else
+               drm_framebuffer_unreference(fb);
 
        return 0;
 
@@ -3509,7 +3839,6 @@ out_err1:
        return ret;
 }
 
-
 /**
  * drm_fb_release - remove and free the FBs on this file
  * @priv: drm file for the ioctl
@@ -3524,6 +3853,9 @@ out_err1:
 void drm_fb_release(struct drm_file *priv)
 {
        struct drm_framebuffer *fb, *tfb;
+       struct drm_mode_rmfb_work arg;
+
+       INIT_LIST_HEAD(&arg.fbs);
 
        /*
         * When the file gets released that means no one else can access the fb
@@ -3536,10 +3868,22 @@ void drm_fb_release(struct drm_file *priv)
         * at it any more.
         */
        list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) {
-               list_del_init(&fb->filp_head);
+               if (atomic_read(&fb->refcount.refcount) > 1) {
+                       list_move_tail(&fb->filp_head, &arg.fbs);
+               } else {
+                       list_del_init(&fb->filp_head);
 
-               /* This drops the fpriv->fbs reference. */
-               drm_framebuffer_unreference(fb);
+                       /* This drops the fpriv->fbs reference. */
+                       drm_framebuffer_unreference(fb);
+               }
+       }
+
+       if (!list_empty(&arg.fbs)) {
+               INIT_WORK_ONSTACK(&arg.work, drm_mode_rmfb_work_fn);
+
+               schedule_work(&arg.work);
+               flush_work(&arg.work);
+               destroy_work_on_stack(&arg.work);
        }
 }
 
@@ -4983,6 +5327,20 @@ int drm_mode_connector_attach_encoder(struct drm_connector *connector,
 {
        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;
@@ -5183,6 +5541,9 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
        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;
@@ -5531,6 +5892,12 @@ int drm_format_num_planes(uint32_t format)
        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;
@@ -5539,14 +5906,14 @@ int drm_format_num_planes(uint32_t format)
 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;
@@ -5559,14 +5926,21 @@ int drm_format_plane_cpp(uint32_t format, int plane)
        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:
@@ -5577,12 +5951,26 @@ int drm_format_plane_cpp(uint32_t format, int plane)
        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);
 
 /**
@@ -5609,6 +5997,10 @@ int drm_format_horz_chroma_subsampling(uint32_t format)
        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:
@@ -5637,7 +6029,9 @@ int drm_format_vert_chroma_subsampling(uint32_t format)
        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;