drm/i915: fix oops on single crtc devices.
[safe/jmp/linux-2.6] / drivers / gpu / drm / drm_sysfs.c
index 8f93729..101d381 100644 (file)
 
 #include <linux/device.h>
 #include <linux/kdev_t.h>
+#include <linux/gfp.h>
 #include <linux/err.h>
 
+#include "drm_sysfs.h"
 #include "drm_core.h"
 #include "drmP.h"
 
 #define to_drm_minor(d) container_of(d, struct drm_minor, kdev)
 #define to_drm_connector(d) container_of(d, struct drm_connector, kdev)
 
+static struct device_type drm_sysfs_device_minor = {
+       .name = "drm_minor"
+};
+
 /**
- * drm_sysfs_suspend - DRM class suspend hook
+ * drm_class_suspend - DRM class suspend hook
  * @dev: Linux device to suspend
  * @state: power state to enter
  *
  * Just figures out what the actual struct drm_device associated with
  * @dev is and calls its suspend hook, if present.
  */
-static int drm_sysfs_suspend(struct device *dev, pm_message_t state)
+static int drm_class_suspend(struct device *dev, pm_message_t state)
 {
-       struct drm_minor *drm_minor = to_drm_minor(dev);
-       struct drm_device *drm_dev = drm_minor->dev;
-
-       if (drm_minor->type == DRM_MINOR_LEGACY &&
-           !drm_core_check_feature(drm_dev, DRIVER_MODESET) &&
-           drm_dev->driver->suspend)
-               return drm_dev->driver->suspend(drm_dev, state);
-
+       if (dev->type == &drm_sysfs_device_minor) {
+               struct drm_minor *drm_minor = to_drm_minor(dev);
+               struct drm_device *drm_dev = drm_minor->dev;
+
+               if (drm_minor->type == DRM_MINOR_LEGACY &&
+                   !drm_core_check_feature(drm_dev, DRIVER_MODESET) &&
+                   drm_dev->driver->suspend)
+                       return drm_dev->driver->suspend(drm_dev, state);
+       }
        return 0;
 }
 
 /**
- * drm_sysfs_resume - DRM class resume hook
+ * drm_class_resume - DRM class resume hook
  * @dev: Linux device to resume
  *
  * Just figures out what the actual struct drm_device associated with
  * @dev is and calls its resume hook, if present.
  */
-static int drm_sysfs_resume(struct device *dev)
+static int drm_class_resume(struct device *dev)
 {
-       struct drm_minor *drm_minor = to_drm_minor(dev);
-       struct drm_device *drm_dev = drm_minor->dev;
-
-       if (drm_minor->type == DRM_MINOR_LEGACY &&
-           !drm_core_check_feature(drm_dev, DRIVER_MODESET) &&
-           drm_dev->driver->resume)
-               return drm_dev->driver->resume(drm_dev);
-
+       if (dev->type == &drm_sysfs_device_minor) {
+               struct drm_minor *drm_minor = to_drm_minor(dev);
+               struct drm_device *drm_dev = drm_minor->dev;
+
+               if (drm_minor->type == DRM_MINOR_LEGACY &&
+                   !drm_core_check_feature(drm_dev, DRIVER_MODESET) &&
+                   drm_dev->driver->resume)
+                       return drm_dev->driver->resume(drm_dev);
+       }
        return 0;
 }
 
-/* Display the version of drm_core. This doesn't work right in current design */
-static ssize_t version_show(struct class *dev, char *buf)
+static char *drm_devnode(struct device *dev, mode_t *mode)
 {
-       return sprintf(buf, "%s %d.%d.%d %s\n", CORE_NAME, CORE_MAJOR,
-                      CORE_MINOR, CORE_PATCHLEVEL, CORE_DATE);
+       return kasprintf(GFP_KERNEL, "dri/%s", dev_name(dev));
 }
 
-static CLASS_ATTR(version, S_IRUGO, version_show, NULL);
+static CLASS_ATTR_STRING(version, S_IRUGO,
+               CORE_NAME " "
+               __stringify(CORE_MAJOR) "."
+               __stringify(CORE_MINOR) "."
+               __stringify(CORE_PATCHLEVEL) " "
+               CORE_DATE);
 
 /**
  * drm_sysfs_create - create a struct drm_sysfs_class structure
@@ -94,13 +105,15 @@ struct class *drm_sysfs_create(struct module *owner, char *name)
                goto err_out;
        }
 
-       class->suspend = drm_sysfs_suspend;
-       class->resume = drm_sysfs_resume;
+       class->suspend = drm_class_suspend;
+       class->resume = drm_class_resume;
 
-       err = class_create_file(class, &class_attr_version);
+       err = class_create_file(class, &class_attr_version.attr);
        if (err)
                goto err_out_class;
 
+       class->devnode = drm_devnode;
+
        return class;
 
 err_out_class:
@@ -118,7 +131,7 @@ void drm_sysfs_destroy(void)
 {
        if ((drm_class == NULL) || (IS_ERR(drm_class)))
                return;
-       class_remove_file(drm_class, &class_attr_version);
+       class_remove_file(drm_class, &class_attr_version.attr);
        class_destroy(drm_class);
 }
 
@@ -147,7 +160,7 @@ static ssize_t status_show(struct device *device,
        enum drm_connector_status status;
 
        status = connector->funcs->detect(connector);
-       return snprintf(buf, PAGE_SIZE, "%s",
+       return snprintf(buf, PAGE_SIZE, "%s\n",
                        drm_get_connector_status_name(status));
 }
 
@@ -166,7 +179,7 @@ static ssize_t dpms_show(struct device *device,
        if (ret)
                return 0;
 
-       return snprintf(buf, PAGE_SIZE, "%s",
+       return snprintf(buf, PAGE_SIZE, "%s\n",
                        drm_get_dpms_name((int)dpms_status));
 }
 
@@ -176,12 +189,13 @@ static ssize_t enabled_show(struct device *device,
 {
        struct drm_connector *connector = to_drm_connector(device);
 
-       return snprintf(buf, PAGE_SIZE, connector->encoder ? "enabled" :
+       return snprintf(buf, PAGE_SIZE, "%s\n", connector->encoder ? "enabled" :
                        "disabled");
 }
 
-static ssize_t edid_show(struct kobject *kobj, struct bin_attribute *attr,
-                        char *buf, loff_t off, size_t count)
+static ssize_t edid_show(struct file *filp, struct kobject *kobj,
+                        struct bin_attribute *attr, char *buf, loff_t off,
+                        size_t count)
 {
        struct device *connector_dev = container_of(kobj, struct device, kobj);
        struct drm_connector *connector = to_drm_connector(connector_dev);
@@ -240,6 +254,7 @@ static ssize_t subconnector_show(struct device *device,
                case DRM_MODE_CONNECTOR_Composite:
                case DRM_MODE_CONNECTOR_SVIDEO:
                case DRM_MODE_CONNECTOR_Component:
+               case DRM_MODE_CONNECTOR_TV:
                        prop = dev->mode_config.tv_subconnector_property;
                        is_tv = 1;
                        break;
@@ -280,6 +295,7 @@ static ssize_t select_subconnector_show(struct device *device,
                case DRM_MODE_CONNECTOR_Composite:
                case DRM_MODE_CONNECTOR_SVIDEO:
                case DRM_MODE_CONNECTOR_Component:
+               case DRM_MODE_CONNECTOR_TV:
                        prop = dev->mode_config.tv_select_subconnector_property;
                        is_tv = 1;
                        break;
@@ -317,7 +333,8 @@ static struct device_attribute connector_attrs_opt1[] = {
 
 static struct bin_attribute edid_attr = {
        .attr.name = "edid",
-       .size = 128,
+       .attr.mode = 0444,
+       .size = 0,
        .read = edid_show,
 };
 
@@ -338,7 +355,10 @@ static struct bin_attribute edid_attr = {
 int drm_sysfs_connector_add(struct drm_connector *connector)
 {
        struct drm_device *dev = connector->dev;
-       int ret = 0, i, j;
+       int attr_cnt = 0;
+       int opt_cnt = 0;
+       int i;
+       int ret = 0;
 
        /* We shouldn't get called more than once for the same connector */
        BUG_ON(device_is_registered(&connector->kdev));
@@ -361,8 +381,8 @@ int drm_sysfs_connector_add(struct drm_connector *connector)
 
        /* Standard attributes */
 
-       for (i = 0; i < ARRAY_SIZE(connector_attrs); i++) {
-               ret = device_create_file(&connector->kdev, &connector_attrs[i]);
+       for (attr_cnt = 0; attr_cnt < ARRAY_SIZE(connector_attrs); attr_cnt++) {
+               ret = device_create_file(&connector->kdev, &connector_attrs[attr_cnt]);
                if (ret)
                        goto err_out_files;
        }
@@ -377,8 +397,9 @@ int drm_sysfs_connector_add(struct drm_connector *connector)
                case DRM_MODE_CONNECTOR_Composite:
                case DRM_MODE_CONNECTOR_SVIDEO:
                case DRM_MODE_CONNECTOR_Component:
-                       for (i = 0; i < ARRAY_SIZE(connector_attrs_opt1); i++) {
-                               ret = device_create_file(&connector->kdev, &connector_attrs_opt1[i]);
+               case DRM_MODE_CONNECTOR_TV:
+                       for (opt_cnt = 0; opt_cnt < ARRAY_SIZE(connector_attrs_opt1); opt_cnt++) {
+                               ret = device_create_file(&connector->kdev, &connector_attrs_opt1[opt_cnt]);
                                if (ret)
                                        goto err_out_files;
                        }
@@ -397,10 +418,10 @@ int drm_sysfs_connector_add(struct drm_connector *connector)
        return 0;
 
 err_out_files:
-       if (i > 0)
-               for (j = 0; j < i; j++)
-                       device_remove_file(&connector->kdev,
-                                          &connector_attrs[i]);
+       for (i = 0; i < opt_cnt; i++)
+               device_remove_file(&connector->kdev, &connector_attrs_opt1[i]);
+       for (i = 0; i < attr_cnt; i++)
+               device_remove_file(&connector->kdev, &connector_attrs[i]);
        device_unregister(&connector->kdev);
 
 out:
@@ -472,6 +493,7 @@ int drm_sysfs_device_add(struct drm_minor *minor)
        minor->kdev.class = drm_class;
        minor->kdev.release = drm_sysfs_device_release;
        minor->kdev.devt = minor->device;
+       minor->kdev.type = &drm_sysfs_device_minor;
        if (minor->type == DRM_MINOR_CONTROL)
                minor_str = "controlD%d";
         else if (minor->type == DRM_MINOR_RENDER)
@@ -504,3 +526,27 @@ void drm_sysfs_device_remove(struct drm_minor *minor)
 {
        device_unregister(&minor->kdev);
 }
+
+
+/**
+ * drm_class_device_register - Register a struct device in the drm class.
+ *
+ * @dev: pointer to struct device to register.
+ *
+ * @dev should have all relevant members pre-filled with the exception
+ * of the class member. In particular, the device_type member must
+ * be set.
+ */
+
+int drm_class_device_register(struct device *dev)
+{
+       dev->class = drm_class;
+       return device_register(dev);
+}
+EXPORT_SYMBOL_GPL(drm_class_device_register);
+
+void drm_class_device_unregister(struct device *dev)
+{
+       return device_unregister(dev);
+}
+EXPORT_SYMBOL_GPL(drm_class_device_unregister);