diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 35ea15d..21c6d4b 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -274,6 +274,74 @@ const char *drm_get_connector_status_name(enum drm_connector_status status)
 }
 EXPORT_SYMBOL(drm_get_connector_status_name);
 
+/*
+ * Default set of pixel formats supported by primary planes.  This matches the
+ * set of formats accepted by the traditional modesetting interfaces; drivers
+ * need only provide their own format list if it differs from the default.
+ */
+static const uint32_t default_primary_plane_formats[] = {
+	DRM_FORMAT_C8,
+	DRM_FORMAT_RGB332,
+	DRM_FORMAT_BGR233,
+	DRM_FORMAT_XRGB4444,
+	DRM_FORMAT_XBGR4444,
+	DRM_FORMAT_RGBX4444,
+	DRM_FORMAT_BGRX4444,
+	DRM_FORMAT_ARGB4444,
+	DRM_FORMAT_ABGR4444,
+	DRM_FORMAT_RGBA4444,
+	DRM_FORMAT_BGRA4444,
+	DRM_FORMAT_XRGB1555,
+	DRM_FORMAT_XBGR1555,
+	DRM_FORMAT_RGBX5551,
+	DRM_FORMAT_BGRX5551,
+	DRM_FORMAT_ARGB1555,
+	DRM_FORMAT_ABGR1555,
+	DRM_FORMAT_RGBA5551,
+	DRM_FORMAT_BGRA5551,
+	DRM_FORMAT_RGB565,
+	DRM_FORMAT_BGR565,
+	DRM_FORMAT_RGB888,
+	DRM_FORMAT_BGR888,
+	DRM_FORMAT_XRGB8888,
+	DRM_FORMAT_XBGR8888,
+	DRM_FORMAT_RGBX8888,
+	DRM_FORMAT_BGRX8888,
+	DRM_FORMAT_ARGB8888,
+	DRM_FORMAT_ABGR8888,
+	DRM_FORMAT_RGBA8888,
+	DRM_FORMAT_BGRA8888,
+	DRM_FORMAT_XRGB2101010,
+	DRM_FORMAT_XBGR2101010,
+	DRM_FORMAT_RGBX1010102,
+	DRM_FORMAT_BGRX1010102,
+	DRM_FORMAT_ARGB2101010,
+	DRM_FORMAT_ABGR2101010,
+	DRM_FORMAT_RGBA1010102,
+	DRM_FORMAT_BGRA1010102,
+	DRM_FORMAT_YUYV,
+	DRM_FORMAT_YVYU,
+	DRM_FORMAT_UYVY,
+	DRM_FORMAT_VYUY,
+	DRM_FORMAT_AYUV,
+	DRM_FORMAT_NV12,
+	DRM_FORMAT_NV21,
+	DRM_FORMAT_NV16,
+	DRM_FORMAT_NV61,
+	DRM_FORMAT_NV24,
+	DRM_FORMAT_NV42,
+	DRM_FORMAT_YUV410,
+	DRM_FORMAT_YVU410,
+	DRM_FORMAT_YUV411,
+	DRM_FORMAT_YVU411,
+	DRM_FORMAT_YUV420,
+	DRM_FORMAT_YVU420,
+	DRM_FORMAT_YUV422,
+	DRM_FORMAT_YVU422,
+	DRM_FORMAT_YUV444,
+	DRM_FORMAT_YVU444,
+};
+
 /**
  * drm_get_subpixel_order_name - return a string for a given subpixel enum
  * @order: enum of subpixel_order
@@ -921,7 +989,7 @@ void drm_encoder_cleanup(struct drm_encoder *encoder)
 EXPORT_SYMBOL(drm_encoder_cleanup);
 
 /**
- * drm_plane_init - Initialise a new plane object
+ * drm_plane_init - Initialise a new sprite plane object
  * @dev: DRM device
  * @plane: plane object to init
  * @possible_crtcs: bitmask of possible CRTCs
@@ -930,7 +998,9 @@ EXPORT_SYMBOL(drm_encoder_cleanup);
  * @format_count: number of elements in @formats
  * @priv: plane is private (hidden from userspace)?
  *
- * Inits a new object created as base part of a driver plane object.
+ * Inits a new object created as base part of a driver plane object.  This
+ * function should only be called for traditional sprite/overlay planes.
+ * Primary planes should be initialized via @drm_plane_set_primary.
  *
  * RETURNS:
  * Zero on success, error code on failure.
@@ -984,6 +1054,74 @@ int drm_plane_init(struct drm_device *dev, struct drm_plane *plane,
 EXPORT_SYMBOL(drm_plane_init);
 
 /**
+ * drm_plane_set_primary - Supply a primary plane for a CRTC
+ * @dev DRM device
+ * @plane: plane object representing the primary plane
+ * @crtc: CRTC this plane is associated with
+ * @funcs: callbacks for the new plane
+ * @formats: array of supported formats (%DRM_FORMAT_*).  If NULL, the
+ *           default list of formats traditionally supported by modesetting
+ *           API's will be used and @format_count will be ignored.
+ * @format_count: number of elements in @formats
+ *
+ * Provides a drm_plane representing a CRTC's primary plane.  This plane will
+ * only be exposed to clients that set the DRM_CLIENT_CAP_EXPOSE_PRIMARY_PLANES
+ * capability bit.
+ *
+ * RETURNS:
+ * Zero on success, error code on failure.
+ */
+int drm_plane_set_primary(struct drm_device *dev, struct drm_plane *plane,
+			  struct drm_crtc *crtc,
+			  const struct drm_plane_funcs *funcs,
+			  const uint32_t *formats, uint32_t format_count)
+{
+	int ret;
+
+	drm_modeset_lock_all(dev);
+
+	ret = drm_mode_object_get(dev, &plane->base, DRM_MODE_OBJECT_PLANE);
+	if (ret)
+		goto out;
+
+	if (formats == NULL) {
+		formats = default_primary_plane_formats;
+		format_count = ARRAY_SIZE(default_primary_plane_formats);
+	}
+
+	plane->base.properties = &plane->properties;
+	plane->dev = dev;
+	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 setting up primary plane\n");
+		drm_mode_object_put(dev, &plane->base);
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	memcpy(plane->format_types, formats, format_count * sizeof(uint32_t));
+	plane->format_count = format_count;
+	plane->possible_crtcs = drm_crtc_mask(crtc);
+
+	/*
+	 * Primary planes are not added to the general plane list, only linked
+	 * to their CRTC.  They will only be advertised to userspace clients
+	 * that indicate support.
+	 */
+	crtc->primary_plane = plane;
+	dev->mode_config.num_primary_plane++;
+	INIT_LIST_HEAD(&plane->head);
+
+ out:
+	drm_modeset_unlock_all(dev);
+
+	return ret;
+}
+EXPORT_SYMBOL(drm_plane_set_primary);
+
+/**
  * drm_plane_cleanup - Clean up the core plane usage
  * @plane: plane to cleanup
  *
@@ -1836,8 +1974,10 @@ int drm_mode_getplane_res(struct drm_device *dev, void *data,
 	struct drm_mode_get_plane_res *plane_resp = data;
 	struct drm_mode_config *config;
 	struct drm_plane *plane;
+	struct drm_crtc *crtc;
 	uint32_t __user *plane_ptr;
 	int copied = 0, ret = 0;
+	unsigned num_planes;
 
 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
 		return -EINVAL;
@@ -1845,14 +1985,32 @@ int drm_mode_getplane_res(struct drm_device *dev, void *data,
 	drm_modeset_lock_all(dev);
 	config = &dev->mode_config;
 
+	num_planes = config->num_plane;
+	if (file_priv->expose_primary_planes)
+		num_planes += config->num_primary_plane;
+
 	/*
 	 * This ioctl is called twice, once to determine how much space is
 	 * needed, and the 2nd time to fill it.
 	 */
-	if (config->num_plane &&
-	    (plane_resp->count_planes >= config->num_plane)) {
+	if (num_planes &&
+	    (plane_resp->count_planes >= num_planes)) {
 		plane_ptr = (uint32_t __user *)(unsigned long)plane_resp->plane_id_ptr;
 
+		if (file_priv->expose_primary_planes) {
+			list_for_each_entry(crtc, &config->crtc_list, head) {
+				if (!crtc->primary_plane)
+					continue;
+
+				if (put_user(crtc->primary_plane->base.id,
+					     plane_ptr + copied)) {
+					ret = -EFAULT;
+					goto out;
+				}
+				copied++;
+			}
+		}
+
 		list_for_each_entry(plane, &config->plane_list, head) {
 			if (put_user(plane->base.id, plane_ptr + copied)) {
 				ret = -EFAULT;
@@ -1861,7 +2019,7 @@ int drm_mode_getplane_res(struct drm_device *dev, void *data,
 			copied++;
 		}
 	}
-	plane_resp->count_planes = config->num_plane;
+	plane_resp->count_planes = num_planes;
 
 out:
 	drm_modeset_unlock_all(dev);
diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c
index dffc836..03579d6 100644
--- a/drivers/gpu/drm/drm_ioctl.c
+++ b/drivers/gpu/drm/drm_ioctl.c
@@ -316,6 +316,11 @@ drm_setclientcap(struct drm_device *dev, void *data, struct drm_file *file_priv)
 			return -EINVAL;
 		file_priv->stereo_allowed = req->value;
 		break;
+	case DRM_CLIENT_CAP_EXPOSE_PRIMARY_PLANES:
+		if (req->value > 1)
+			return -EINVAL;
+		file_priv->expose_primary_planes = req->value;
+		break;
 	default:
 		return -EINVAL;
 	}
diff --git a/include/drm/drmP.h b/include/drm/drmP.h
index 04a7f31..997fa29 100644
--- a/include/drm/drmP.h
+++ b/include/drm/drmP.h
@@ -437,6 +437,8 @@ struct drm_file {
 	unsigned is_master :1; /* this file private is a master for a minor */
 	/* true when the client has asked us to expose stereo 3D mode flags */
 	unsigned stereo_allowed :1;
+	/* true if client understands CRTC primary planes in the plane list */
+	unsigned expose_primary_planes:1;
 
 	struct pid *pid;
 	kuid_t uid;
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index ce9ee60..33a955b 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -429,6 +429,11 @@ struct drm_crtc {
 	 * by drm_mode_set_config_internal to implement correct refcounting. */
 	struct drm_framebuffer *old_fb;
 
+	/* Primary plane to expose to userspace if the client sets the 'expose
+	 * primary planes' cap.
+	 */
+	struct drm_plane *primary_plane;
+
 	bool enabled;
 
 	/* Requested mode from modesetting. */
@@ -859,6 +864,7 @@ struct drm_mode_config {
 	int num_plane;
 	struct list_head plane_list;
 
+	int num_primary_plane;
 	int num_crtc;
 	struct list_head crtc_list;
 
@@ -984,6 +990,11 @@ extern int drm_plane_init(struct drm_device *dev,
 			  const struct drm_plane_funcs *funcs,
 			  const uint32_t *formats, uint32_t format_count,
 			  bool priv);
+extern int drm_plane_set_primary(struct drm_device *dev,
+				 struct drm_plane *plane, struct drm_crtc *crtc,
+				 const struct drm_plane_funcs *funcs,
+				 const uint32_t *formats,
+				 uint32_t format_count);
 extern void drm_plane_cleanup(struct drm_plane *plane);
 extern void drm_plane_force_disable(struct drm_plane *plane);
 
diff --git a/include/uapi/drm/drm.h b/include/uapi/drm/drm.h
index 3c9a833..58a2468 100644
--- a/include/uapi/drm/drm.h
+++ b/include/uapi/drm/drm.h
@@ -635,6 +635,14 @@ struct drm_get_cap {
  */
 #define DRM_CLIENT_CAP_STEREO_3D	1
 
+/**
+ * DRM_CLIENT_CAP_EXPOSE_PRIMARY_PLANES
+ *
+ * If set to 1, the DRM core will expose CRTC primary planes along with
+ * sprite/overlay planes.
+ */
+#define DRM_CLIENT_CAP_EXPOSE_PRIMARY_PLANES  2
+
 /** DRM_IOCTL_SET_CLIENT_CAP ioctl argument type */
 struct drm_set_client_cap {
 	__u64 capability;
