[modesetting-101] Add subconnector and select_subconnector properties.

- These facilitate DVI-I and tv-out that can drive multiple types of signals.
main
Maarten Maathuis 2008-07-04 17:17:11 +02:00
parent 142a309604
commit b29578103f
7 changed files with 300 additions and 17 deletions

View 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" },
@ -497,6 +539,38 @@ static int drm_mode_create_standard_connector_properties(struct drm_device *dev)
return 0;
}
/**
* 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
@ -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

View 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);

View 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;

View 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);

View 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);

View 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;

View 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);