Merge tag 'topic/core-stuff-2014-12-10' of git://anongit.freedesktop.org/drm-intel...
[pandora-kernel.git] / drivers / gpu / drm / drm_crtc.c
index 632e6ea..5213da4 100644 (file)
@@ -910,6 +910,11 @@ void drm_connector_cleanup(struct drm_connector *connector)
        struct drm_device *dev = connector->dev;
        struct drm_display_mode *mode, *t;
 
+       if (connector->tile_group) {
+               drm_mode_put_tile_group(dev, connector->tile_group);
+               connector->tile_group = NULL;
+       }
+
        list_for_each_entry_safe(mode, t, &connector->probed_modes, head)
                drm_mode_remove(connector, mode);
 
@@ -1348,6 +1353,11 @@ static int drm_mode_create_standard_connector_properties(struct drm_device *dev)
                                       "PATH", 0);
        dev->mode_config.path_property = dev_path;
 
+       dev->mode_config.tile_property = drm_property_create(dev,
+                                                            DRM_MODE_PROP_BLOB |
+                                                            DRM_MODE_PROP_IMMUTABLE,
+                                                            "TILE", 0);
+
        return 0;
 }
 
@@ -4091,6 +4101,52 @@ int drm_mode_connector_set_path_property(struct drm_connector *connector,
 }
 EXPORT_SYMBOL(drm_mode_connector_set_path_property);
 
+/**
+ * drm_mode_connector_set_tile_property - set tile property on connector
+ * @connector: connector to set property on.
+ *
+ * This looks up the tile information for a connector, and creates a
+ * property for userspace to parse if it exists. The property is of
+ * the form of 8 integers using ':' as a separator.
+ *
+ * Returns:
+ * Zero on success, errno on failure.
+ */
+int drm_mode_connector_set_tile_property(struct drm_connector *connector)
+{
+       struct drm_device *dev = connector->dev;
+       int ret, size;
+       char tile[256];
+
+       if (connector->tile_blob_ptr)
+               drm_property_destroy_blob(dev, connector->tile_blob_ptr);
+
+       if (!connector->has_tile) {
+               connector->tile_blob_ptr = NULL;
+               ret = drm_object_property_set_value(&connector->base,
+                                                   dev->mode_config.tile_property, 0);
+               return ret;
+       }
+
+       snprintf(tile, 256, "%d:%d:%d:%d:%d:%d:%d:%d",
+                connector->tile_group->id, connector->tile_is_single_monitor,
+                connector->num_h_tile, connector->num_v_tile,
+                connector->tile_h_loc, connector->tile_v_loc,
+                connector->tile_h_size, connector->tile_v_size);
+       size = strlen(tile) + 1;
+
+       connector->tile_blob_ptr = drm_property_create_blob(connector->dev,
+                                                           size, tile);
+       if (!connector->tile_blob_ptr)
+               return -EINVAL;
+
+       ret = drm_object_property_set_value(&connector->base,
+                                           dev->mode_config.tile_property,
+                                           connector->tile_blob_ptr->base.id);
+       return ret;
+}
+EXPORT_SYMBOL(drm_mode_connector_set_tile_property);
+
 /**
  * drm_mode_connector_update_edid_property - update the edid property of a connector
  * @connector: drm connector
@@ -5161,6 +5217,7 @@ void drm_mode_config_init(struct drm_device *dev)
        INIT_LIST_HEAD(&dev->mode_config.property_blob_list);
        INIT_LIST_HEAD(&dev->mode_config.plane_list);
        idr_init(&dev->mode_config.crtc_idr);
+       idr_init(&dev->mode_config.tile_idr);
 
        drm_modeset_lock_all(dev);
        drm_mode_create_standard_connector_properties(dev);
@@ -5248,6 +5305,7 @@ void drm_mode_config_cleanup(struct drm_device *dev)
                crtc->funcs->destroy(crtc);
        }
 
+       idr_destroy(&dev->mode_config.tile_idr);
        idr_destroy(&dev->mode_config.crtc_idr);
        drm_modeset_lock_fini(&dev->mode_config.connection_mutex);
 }
@@ -5270,3 +5328,100 @@ struct drm_property *drm_mode_create_rotation_property(struct drm_device *dev,
                                           supported_rotations);
 }
 EXPORT_SYMBOL(drm_mode_create_rotation_property);
+
+/**
+ * DOC: Tile group
+ *
+ * Tile groups are used to represent tiled monitors with a unique
+ * integer identifier. Tiled monitors using DisplayID v1.3 have
+ * a unique 8-byte handle, we store this in a tile group, so we
+ * have a common identifier for all tiles in a monitor group.
+ */
+static void drm_tile_group_free(struct kref *kref)
+{
+       struct drm_tile_group *tg = container_of(kref, struct drm_tile_group, refcount);
+       struct drm_device *dev = tg->dev;
+       mutex_lock(&dev->mode_config.idr_mutex);
+       idr_remove(&dev->mode_config.tile_idr, tg->id);
+       mutex_unlock(&dev->mode_config.idr_mutex);
+       kfree(tg);
+}
+
+/**
+ * drm_mode_put_tile_group - drop a reference to a tile group.
+ * @dev: DRM device
+ * @tg: tile group to drop reference to.
+ *
+ * drop reference to tile group and free if 0.
+ */
+void drm_mode_put_tile_group(struct drm_device *dev,
+                            struct drm_tile_group *tg)
+{
+       kref_put(&tg->refcount, drm_tile_group_free);
+}
+
+/**
+ * drm_mode_get_tile_group - get a reference to an existing tile group
+ * @dev: DRM device
+ * @topology: 8-bytes unique per monitor.
+ *
+ * Use the unique bytes to get a reference to an existing tile group.
+ *
+ * RETURNS:
+ * tile group or NULL if not found.
+ */
+struct drm_tile_group *drm_mode_get_tile_group(struct drm_device *dev,
+                                              char topology[8])
+{
+       struct drm_tile_group *tg;
+       int id;
+       mutex_lock(&dev->mode_config.idr_mutex);
+       idr_for_each_entry(&dev->mode_config.tile_idr, tg, id) {
+               if (!memcmp(tg->group_data, topology, 8)) {
+                       if (!kref_get_unless_zero(&tg->refcount))
+                               tg = NULL;
+                       mutex_unlock(&dev->mode_config.idr_mutex);
+                       return tg;
+               }
+       }
+       mutex_unlock(&dev->mode_config.idr_mutex);
+       return NULL;
+}
+
+/**
+ * drm_mode_create_tile_group - create a tile group from a displayid description
+ * @dev: DRM device
+ * @topology: 8-bytes unique per monitor.
+ *
+ * Create a tile group for the unique monitor, and get a unique
+ * identifier for the tile group.
+ *
+ * RETURNS:
+ * new tile group or error.
+ */
+struct drm_tile_group *drm_mode_create_tile_group(struct drm_device *dev,
+                                                 char topology[8])
+{
+       struct drm_tile_group *tg;
+       int ret;
+
+       tg = kzalloc(sizeof(*tg), GFP_KERNEL);
+       if (!tg)
+               return ERR_PTR(-ENOMEM);
+
+       kref_init(&tg->refcount);
+       memcpy(tg->group_data, topology, 8);
+       tg->dev = dev;
+
+       mutex_lock(&dev->mode_config.idr_mutex);
+       ret = idr_alloc(&dev->mode_config.tile_idr, tg, 1, 0, GFP_KERNEL);
+       if (ret >= 0) {
+               tg->id = ret;
+       } else {
+               kfree(tg);
+               tg = ERR_PTR(ret);
+       }
+
+       mutex_unlock(&dev->mode_config.idr_mutex);
+       return tg;
+}