[modesetting-101] Add subconnector and select_subconnector properties.
authorMaarten Maathuis <madman2003@gmail.com>
Fri, 4 Jul 2008 15:17:11 +0000 (17:17 +0200)
committerMaarten Maathuis <madman2003@gmail.com>
Fri, 4 Jul 2008 15:19:11 +0000 (17:19 +0200)
- These facilitate DVI-I and tv-out that can drive multiple types of signals.

linux-core/drm_crtc.c
linux-core/drm_crtc.h
linux-core/drm_sysfs.c
linux-core/intel_tv.c
linux-core/nv50_connector.h
linux-core/nv50_kms_wrapper.c
linux-core/nv50_kms_wrapper.h

index ca5e75a..1a381ab 100644 (file)
@@ -60,6 +60,48 @@ char *drm_get_dpms_name(int val)
        return "unknown";
 }
 
+static struct drm_prop_enum_list drm_select_subconnector_enum_list[] =
+{
+       { DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */
+       { DRM_MODE_SUBCONNECTOR_DVID,      "DVI-D"     }, /* DVI-I  */
+       { DRM_MODE_SUBCONNECTOR_DVIA,      "DVI-A"     }, /* DVI-I  */
+       { DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */
+       { DRM_MODE_SUBCONNECTOR_SVIDEO,    "SVIDEO"    }, /* TV-out */
+       { DRM_MODE_SUBCONNECTOR_Component, "Component" }, /* TV-out */
+};
+
+char *drm_get_select_subconnector_name(int val)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(drm_select_subconnector_enum_list); i++)
+               if (drm_select_subconnector_enum_list[i].type == val)
+                       return drm_select_subconnector_enum_list[i].name;
+
+       return "unknown";
+}
+
+static struct drm_prop_enum_list drm_subconnector_enum_list[] =
+{
+       { DRM_MODE_SUBCONNECTOR_Unknown,   "Unknown"    }, /* DVI-I and TV-out */
+       { DRM_MODE_SUBCONNECTOR_DVID,      "DVI-D"     }, /* DVI-I  */
+       { DRM_MODE_SUBCONNECTOR_DVIA,      "DVI-A"     }, /* DVI-I  */
+       { DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */
+       { DRM_MODE_SUBCONNECTOR_SVIDEO,    "SVIDEO"    }, /* TV-out */
+       { DRM_MODE_SUBCONNECTOR_Component, "Component" }, /* TV-out */
+};
+
+char *drm_get_subconnector_name(int val)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(drm_subconnector_enum_list); i++)
+               if (drm_subconnector_enum_list[i].type == val)
+                       return drm_subconnector_enum_list[i].name;
+
+       return "unknown";
+}
+
 static struct drm_prop_enum_list drm_connector_enum_list[] = 
 {      { DRM_MODE_CONNECTOR_Unknown, "Unknown" },
        { DRM_MODE_CONNECTOR_VGA, "VGA" },
@@ -498,6 +540,38 @@ static int drm_mode_create_standard_connector_properties(struct drm_device *dev)
 }
 
 /**
+ * drm_mode_create_dvi_i_properties - create DVI-I specific connector properties
+ * @dev: DRM device
+ *
+ * Called by a driver the first time a DVI-I connector is made.
+ */
+int drm_mode_create_dvi_i_properties(struct drm_device *dev)
+{
+       int i;
+
+       if (dev->mode_config.dvi_i_select_subconnector_property)
+               return 0;
+
+       dev->mode_config.dvi_i_select_subconnector_property = drm_property_create(dev, DRM_MODE_PROP_ENUM,
+                                   "select subconnector", 3);
+       /* add enum element 0-2 */
+       for (i = 0; i < 3; i++)
+               drm_property_add_enum(dev->mode_config.dvi_i_select_subconnector_property, i, drm_select_subconnector_enum_list[i].type,
+                               drm_select_subconnector_enum_list[i].name);
+
+       /* This is a property which indicates the most likely thing to be connected. */
+       dev->mode_config.dvi_i_subconnector_property = drm_property_create(dev, DRM_MODE_PROP_ENUM | DRM_MODE_PROP_IMMUTABLE,
+                                   "subconnector", 3);
+       /* add enum element 0-2 */
+       for (i = 0; i < 3; i++)
+               drm_property_add_enum(dev->mode_config.dvi_i_subconnector_property, i, drm_subconnector_enum_list[i].type,
+                               drm_subconnector_enum_list[i].name);
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_mode_create_dvi_i_properties);
+
+/**
  * drm_create_tv_properties - create TV specific connector properties
  * @dev: DRM device
  * @num_modes: number of different TV formats (modes) supported
@@ -508,11 +582,29 @@ static int drm_mode_create_standard_connector_properties(struct drm_device *dev)
  * responsible for allocating a list of format names and passing them to
  * this routine.
  */
-bool drm_create_tv_properties(struct drm_device *dev, int num_modes,
+int drm_mode_create_tv_properties(struct drm_device *dev, int num_modes,
                              char *modes[])
 {
        int i;
 
+       if (dev->mode_config.tv_select_subconnector_property) /* already done */
+               return 0;
+
+       dev->mode_config.tv_select_subconnector_property = drm_property_create(dev, DRM_MODE_PROP_ENUM,
+                                   "select subconnector", 4);
+       /* add enum element 3-5 */
+       for (i = 1; i < 4; i++)
+               drm_property_add_enum(dev->mode_config.tv_select_subconnector_property, i, drm_select_subconnector_enum_list[i + 2].type,
+                               drm_select_subconnector_enum_list[i + 2].name);
+
+       /* This is a property which indicates the most likely thing to be connected. */
+       dev->mode_config.tv_subconnector_property = drm_property_create(dev, DRM_MODE_PROP_ENUM | DRM_MODE_PROP_IMMUTABLE,
+                                   "subconnector", 4);
+       /* add enum element 3-5 */
+       for (i = 1; i < 4; i++)
+               drm_property_add_enum(dev->mode_config.tv_subconnector_property, i, drm_subconnector_enum_list[i + 2].type,
+                               drm_subconnector_enum_list[i + 2].name);
+
        dev->mode_config.tv_left_margin_property =
                drm_property_create(dev, DRM_MODE_PROP_RANGE |
                                    DRM_MODE_PROP_IMMUTABLE,
@@ -547,7 +639,7 @@ bool drm_create_tv_properties(struct drm_device *dev, int num_modes,
 
        return 0;
 }
-EXPORT_SYMBOL(drm_create_tv_properties);
+EXPORT_SYMBOL(drm_mode_create_tv_properties);
 
 /**
  * drm_mode_config_init - initialize DRM mode_configuration structure
index 65ff3f2..caceb65 100644 (file)
@@ -170,6 +170,14 @@ struct drm_display_mode {
 #define DPMSModeSuspend 2
 #define DPMSModeOff 3
 
+#define DRM_MODE_SUBCONNECTOR_Automatic 0
+#define DRM_MODE_SUBCONNECTOR_Unknown 0
+#define DRM_MODE_SUBCONNECTOR_DVID 3
+#define DRM_MODE_SUBCONNECTOR_DVIA 4
+#define DRM_MODE_SUBCONNECTOR_Composite 5
+#define DRM_MODE_SUBCONNECTOR_SVIDEO 6
+#define DRM_MODE_SUBCONNECTOR_Component 8
+
 #define DRM_MODE_CONNECTOR_Unknown 0
 #define DRM_MODE_CONNECTOR_VGA 1
 #define DRM_MODE_CONNECTOR_DVII 2
@@ -571,7 +579,13 @@ struct drm_mode_config {
        struct drm_property *edid_property;
        struct drm_property *dpms_property;
 
+       /* optional properties */
+       struct drm_property *dvi_i_subconnector_property;
+       struct drm_property *dvi_i_select_subconnector_property;
+
        /* TV properties */
+       struct drm_property *tv_subconnector_property;
+       struct drm_property *tv_select_subconnector_property;
        struct drm_property *tv_mode_property;
        struct drm_property *tv_left_margin_property;
        struct drm_property *tv_right_margin_property;
@@ -612,6 +626,8 @@ extern void drm_encoder_cleanup(struct drm_encoder *encoder);
 
 extern char *drm_get_connector_name(struct drm_connector *connector);
 extern char *drm_get_dpms_name(int val);
+extern char *drm_get_select_subconnector_name(int val);
+extern char *drm_get_subconnector_name(int val);
 extern void drm_fb_release(struct file *filp);
 extern int drm_mode_group_init_legacy_group(struct drm_device *dev, struct drm_mode_group *group);
 extern struct edid *drm_get_edid(struct drm_connector *connector,
@@ -674,7 +690,8 @@ extern struct drm_property *drm_property_create(struct drm_device *dev, int flag
 extern void drm_property_destroy(struct drm_device *dev, struct drm_property *property);
 extern int drm_property_add_enum(struct drm_property *property, int index, 
                                 uint64_t value, const char *name);
-extern bool drm_create_tv_properties(struct drm_device *dev, int num_formats,
+extern int drm_mode_create_dvi_i_properties(struct drm_device *dev);
+extern int drm_mode_create_tv_properties(struct drm_device *dev, int num_formats,
                                     char *formats[]);
 extern char *drm_get_encoder_name(struct drm_encoder *encoder);
 
index 36b9222..5c384a6 100644 (file)
@@ -231,6 +231,78 @@ static ssize_t modes_show(struct device *device,
        return written;
 }
 
+static ssize_t subconnector_show(struct device *device,
+                          struct device_attribute *attr,
+                          char *buf)
+{
+       struct drm_connector *connector = container_of(device, struct drm_connector, kdev);
+       struct drm_device *dev = connector->dev;
+       struct drm_property *prop = NULL;
+       uint64_t subconnector;
+       int ret;
+
+       switch (connector->connector_type) {
+               case DRM_MODE_CONNECTOR_DVII:
+                       prop = dev->mode_config.dvi_i_subconnector_property;
+                       break;
+               case DRM_MODE_CONNECTOR_Composite:
+               case DRM_MODE_CONNECTOR_SVIDEO:
+               case DRM_MODE_CONNECTOR_Component:
+                       prop = dev->mode_config.tv_subconnector_property;
+                       break;
+               default:
+                       DRM_ERROR("Wrong connector type for this property\n");
+                       return 0;
+       }
+
+       if (!prop) {
+               DRM_ERROR("Unable to find subconnector property\n");
+               return 0;
+       }
+
+       ret = drm_connector_property_get_value(connector, prop, &subconnector);
+       if (ret)
+               return 0;
+
+       return snprintf(buf, PAGE_SIZE, "%s", drm_get_subconnector_name((int)subconnector));
+}
+
+static ssize_t select_subconnector_show(struct device *device,
+                          struct device_attribute *attr,
+                          char *buf)
+{
+       struct drm_connector *connector = container_of(device, struct drm_connector, kdev);
+       struct drm_device *dev = connector->dev;
+       struct drm_property *prop = NULL;
+       uint64_t subconnector;
+       int ret;
+
+       switch (connector->connector_type) {
+               case DRM_MODE_CONNECTOR_DVII:
+                       prop = dev->mode_config.dvi_i_select_subconnector_property;
+                       break;
+               case DRM_MODE_CONNECTOR_Composite:
+               case DRM_MODE_CONNECTOR_SVIDEO:
+               case DRM_MODE_CONNECTOR_Component:
+                       prop = dev->mode_config.tv_select_subconnector_property;
+                       break;
+               default:
+                       DRM_ERROR("Wrong connector type for this property\n");
+                       return 0;
+       }
+
+       if (!prop) {
+               DRM_ERROR("Unable to find select subconnector property\n");
+               return 0;
+       }
+
+       ret = drm_connector_property_get_value(connector, prop, &subconnector);
+       if (ret)
+               return 0;
+
+       return snprintf(buf, PAGE_SIZE, "%s", drm_get_select_subconnector_name((int)subconnector));
+}
+
 static struct device_attribute connector_attrs[] = {
        __ATTR_RO(status),
        __ATTR_RO(enabled),
@@ -238,6 +310,12 @@ static struct device_attribute connector_attrs[] = {
        __ATTR_RO(modes),
 };
 
+/* These attributes are for both DVI-I connectors and all types of tv-out. */
+static struct device_attribute connector_attrs_opt1[] = {
+       __ATTR_RO(subconnector),
+       __ATTR_RO(select_subconnector),
+};
+
 static struct bin_attribute edid_attr = {
        .attr.name = "edid",
        .size = 128,
@@ -282,12 +360,32 @@ int drm_sysfs_connector_add(struct drm_connector *connector)
                goto out;
        }
 
+       /* Standard attributes */
+
        for (i = 0; i < ARRAY_SIZE(connector_attrs); i++) {
                ret = device_create_file(&connector->kdev, &connector_attrs[i]);
                if (ret)
                        goto err_out_files;
        }
 
+       /* Optional attributes */
+       /* On the long run it maybe a good idea to make one set of optionals per connector type. */
+
+       switch (connector->connector_type) {
+               case DRM_MODE_CONNECTOR_DVII:
+               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]);
+                               if (ret)
+                                       goto err_out_files;
+                       }
+                       break;
+               default:
+                       break;
+       }
+
        ret = sysfs_create_bin_file(&connector->kdev.kobj, &edid_attr);
        if (ret)
                goto err_out_files;
index 39f33d6..f564fa9 100644 (file)
@@ -1713,7 +1713,7 @@ intel_tv_init(struct drm_device *dev)
                goto out;
        for (i = 0; i < NUM_TV_MODES; i++)
                tv_format_names[i] = tv_modes[i].name;
-       drm_create_tv_properties(dev, NUM_TV_MODES, tv_format_names);
+       drm_mode_create_tv_properties(dev, NUM_TV_MODES, tv_format_names);
 
        drm_connector_attach_property(connector, dev->mode_config.tv_mode_property,
                                   initial_mode);
index 484227a..ebd6eac 100644 (file)
@@ -48,7 +48,6 @@ struct nv50_connector {
        struct nv50_output *output;
 
        int scaling_mode;
-       bool digital; /* last connected output, this has to be set from the outside*/
 
        bool (*detect) (struct nv50_connector *connector);
        int (*destroy) (struct nv50_connector *connector);
index f1f5b69..ee18b36 100644 (file)
@@ -387,7 +387,7 @@ int nv50_kms_crtc_set_config(struct drm_mode_set *set)
                        }
                        connector = to_nv50_connector(drm_connector);
 
-                       output = connector->to_output(connector, connector->digital);
+                       output = connector->to_output(connector, nv50_kms_connector_is_digital(drm_connector));
                        if (!output) {
                                DRM_ERROR("No output\n");
                                goto out;
@@ -447,7 +447,7 @@ int nv50_kms_crtc_set_config(struct drm_mode_set *set)
                                goto out;
                        }
 
-                       output = connector->to_output(connector, connector->digital);
+                       output = connector->to_output(connector, nv50_kms_connector_is_digital(drm_connector));
                        if (!output) {
                                DRM_ERROR("No output\n");
                                goto out;
@@ -806,6 +806,63 @@ static int nv50_kms_encoders_init(struct drm_device *dev)
  * Connector functions
  */
 
+bool nv50_kms_connector_is_digital(struct drm_connector *drm_connector)
+{
+       struct drm_device *dev = drm_connector->dev;
+
+       switch (drm_connector->connector_type) {
+               case DRM_MODE_CONNECTOR_VGA:
+               case DRM_MODE_CONNECTOR_SVIDEO:
+                       return false;
+               case DRM_MODE_CONNECTOR_DVID:
+               case DRM_MODE_CONNECTOR_LVDS:
+                       return true;
+               default:
+                       break;
+       }
+
+       if (drm_connector->connector_type == DRM_MODE_CONNECTOR_DVII) {
+               int rval;
+               uint64_t prop_val;
+
+               rval = drm_connector_property_get_value(drm_connector, dev->mode_config.dvi_i_select_subconnector_property, &prop_val);
+               if (!rval) {
+                       DRM_ERROR("Unable to find select subconnector property, defaulting to DVI-D\n");
+                       return true;
+               }
+
+               /* Is a subconnector explicitly selected? */
+               switch (prop_val) {
+                       case DRM_MODE_SUBCONNECTOR_DVID:
+                               return true;
+                       case DRM_MODE_SUBCONNECTOR_DVIA:
+                               return false;
+                       default:
+                               break;
+               }
+
+               rval = drm_connector_property_get_value(drm_connector, dev->mode_config.dvi_i_subconnector_property, &prop_val);
+               if (!rval) {
+                       DRM_ERROR("Unable to find subconnector property, defaulting to DVI-D\n");
+                       return true;
+               }
+
+               /* Do we know what subconnector we currently have connected? */
+               switch (prop_val) {
+                       case DRM_MODE_SUBCONNECTOR_DVID:
+                               return true;
+                       case DRM_MODE_SUBCONNECTOR_DVIA:
+                               return false;
+                       default:
+                               DRM_ERROR("Unknown subconnector value, defaulting to DVI-D\n");
+                               return true;
+               }
+       }
+
+       DRM_ERROR("Unknown connector type, defaulting to analog\n");
+       return false;
+}
+
 void nv50_kms_connector_detect_all(struct drm_device *dev)
 {
        struct drm_connector *drm_connector = NULL;
@@ -867,7 +924,8 @@ static struct drm_display_mode std_mode[] = {
 static void nv50_kms_connector_fill_modes(struct drm_connector *drm_connector, uint32_t maxX, uint32_t maxY)
 {
        struct nv50_connector *connector = to_nv50_connector(drm_connector);
-       int ret = 0;
+       struct drm_device *dev = drm_connector->dev;
+       int rval = 0;
        bool connected;
        struct drm_display_mode *mode, *t;
        struct edid *edid = NULL;
@@ -896,21 +954,32 @@ static void nv50_kms_connector_fill_modes(struct drm_connector *drm_connector, u
 
        if (edid) {
                drm_mode_connector_update_edid_property(drm_connector, edid);
-               ret = drm_add_edid_modes(drm_connector, edid);
-               connector->digital = edid->digital; /* cache */
+               rval = drm_add_edid_modes(drm_connector, edid);
+
+               /* 2 encoders per connector */
+               /* eventually do this based on load detect and hot plug detect */
+               if (drm_connector->connector_type == DRM_MODE_CONNECTOR_DVII) {
+                       uint64_t subtype = 0;
+                       if (edid->digital)
+                               subtype = DRM_MODE_SUBCONNECTOR_DVID;
+                       else
+                               subtype = DRM_MODE_SUBCONNECTOR_DVIA;
+                       drm_connector_property_set_value(drm_connector, dev->mode_config.dvi_i_subconnector_property, subtype);
+               }
+
                kfree(edid);
        }
 
-       if (ret) /* number of modes  > 1 */
+       if (rval) /* number of modes  > 1 */
                drm_mode_connector_list_update(drm_connector);
 
        if (maxX && maxY)
-               drm_mode_validate_size(drm_connector->dev, &drm_connector->modes, maxX, maxY, 0);
+               drm_mode_validate_size(dev, &drm_connector->modes, maxX, maxY, 0);
 
        list_for_each_entry_safe(mode, t, &drm_connector->modes, head) {
                if (mode->status == MODE_OK) {
                        struct nouveau_hw_mode *hw_mode = nv50_kms_to_hw_mode(mode);
-                       struct nv50_output *output = connector->to_output(connector, connector->digital);
+                       struct nv50_output *output = connector->to_output(connector, nv50_kms_connector_is_digital(drm_connector));
 
                        mode->status = output->validate_mode(output, hw_mode);
                        /* find native mode, TODO: also check if we actually found one */
@@ -926,14 +995,14 @@ static void nv50_kms_connector_fill_modes(struct drm_connector *drm_connector, u
        list_for_each_entry_safe(mode, t, &drm_connector->modes, head) {
                if (mode->status == MODE_OK) {
                        struct nouveau_hw_mode *hw_mode = nv50_kms_to_hw_mode(mode);
-                       struct nv50_output *output = connector->to_output(connector, connector->digital);
+                       struct nv50_output *output = connector->to_output(connector, nv50_kms_connector_is_digital(drm_connector));
 
                        mode->status = output->validate_mode(output, hw_mode);
                        kfree(hw_mode);
                }
        }
 
-       drm_mode_prune_invalid(drm_connector->dev, &drm_connector->modes, true);
+       drm_mode_prune_invalid(dev, &drm_connector->modes, true);
 
        if (list_empty(&drm_connector->modes)) {
                struct drm_display_mode *stdmode;
@@ -947,14 +1016,14 @@ static void nv50_kms_connector_fill_modes(struct drm_connector *drm_connector, u
                 * here and bailed in the past, now we add a standard
                 * 640x480@60Hz mode and carry on.
                 */
-               stdmode = drm_mode_duplicate(drm_connector->dev, &std_mode[0]);
+               stdmode = drm_mode_duplicate(dev, &std_mode[0]);
                drm_mode_probed_add(drm_connector, stdmode);
                drm_mode_list_concat(&drm_connector->probed_modes,
                                     &drm_connector->modes);
 
                /* also add it as native mode */
                hw_mode = nv50_kms_to_hw_mode(mode);
-               output = connector->to_output(connector, connector->digital);
+               output = connector->to_output(connector, nv50_kms_connector_is_digital(drm_connector));
 
                if (hw_mode)
                        *output->native_mode = *hw_mode;
@@ -1045,6 +1114,13 @@ static int nv50_kms_connectors_init(struct drm_device *dev)
 
                drm_connector_init(dev, drm_connector, &nv50_kms_connector_funcs, type);
 
+               /* Init DVI-I specific properties */
+               if (type == DRM_MODE_CONNECTOR_DVII) {
+                       drm_mode_create_dvi_i_properties(dev);
+                       drm_connector_attach_property(drm_connector, dev->mode_config.dvi_i_subconnector_property, 0);
+                       drm_connector_attach_property(drm_connector, dev->mode_config.dvi_i_select_subconnector_property, 0);
+               }
+
                /* attach encoders, possibilities are analog + digital */
                for (i = 0; i < 2; i++) {
                        struct drm_encoder *drm_encoder = NULL;
index f224f1b..5ac6652 100644 (file)
@@ -87,6 +87,7 @@ struct nv50_kms_priv {
 
 struct nv50_kms_priv *nv50_get_kms_priv(struct drm_device *dev);
 void nv50_kms_connector_detect_all(struct drm_device *dev);
+bool nv50_kms_connector_is_digital(struct drm_connector *drm_connector);
 
 int nv50_kms_init(struct drm_device *dev);
 int nv50_kms_destroy(struct drm_device *dev);