summaryrefslogtreecommitdiff
path: root/linux-core
diff options
context:
space:
mode:
Diffstat (limited to 'linux-core')
-rw-r--r--linux-core/drm_crtc.c96
-rw-r--r--linux-core/drm_crtc.h19
-rw-r--r--linux-core/drm_sysfs.c98
-rw-r--r--linux-core/intel_tv.c2
-rw-r--r--linux-core/nv50_connector.h1
-rw-r--r--linux-core/nv50_kms_wrapper.c100
-rw-r--r--linux-core/nv50_kms_wrapper.h1
7 files changed, 300 insertions, 17 deletions
diff --git a/linux-core/drm_crtc.c b/linux-core/drm_crtc.c
index ca5e75a4..1a381abe 100644
--- a/linux-core/drm_crtc.c
+++ b/linux-core/drm_crtc.c
@@ -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
diff --git a/linux-core/drm_crtc.h b/linux-core/drm_crtc.h
index 65ff3f29..caceb650 100644
--- a/linux-core/drm_crtc.h
+++ b/linux-core/drm_crtc.h
@@ -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);
diff --git a/linux-core/drm_sysfs.c b/linux-core/drm_sysfs.c
index 36b92224..5c384a60 100644
--- a/linux-core/drm_sysfs.c
+++ b/linux-core/drm_sysfs.c
@@ -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;
diff --git a/linux-core/intel_tv.c b/linux-core/intel_tv.c
index 39f33d6c..f564fa91 100644
--- a/linux-core/intel_tv.c
+++ b/linux-core/intel_tv.c
@@ -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);
diff --git a/linux-core/nv50_connector.h b/linux-core/nv50_connector.h
index 484227a0..ebd6eac6 100644
--- a/linux-core/nv50_connector.h
+++ b/linux-core/nv50_connector.h
@@ -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);
diff --git a/linux-core/nv50_kms_wrapper.c b/linux-core/nv50_kms_wrapper.c
index f1f5b69f..ee18b36a 100644
--- a/linux-core/nv50_kms_wrapper.c
+++ b/linux-core/nv50_kms_wrapper.c
@@ -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;
diff --git a/linux-core/nv50_kms_wrapper.h b/linux-core/nv50_kms_wrapper.h
index f224f1bb..5ac66522 100644
--- a/linux-core/nv50_kms_wrapper.h
+++ b/linux-core/nv50_kms_wrapper.h
@@ -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);