Commit 0294cf4f authored by Alex Deucher's avatar Alex Deucher Committed by Dave Airlie

drm/radeon/kms: fix connector edid handling

Based partly on a patch from
Christian Koenig <deathsimple@vodafone.de>

- fix several memory leaks in radeon_connector->edid handling
- store edid in radeon_connector->edid in detect() or get_modes()
- switch hdmi detect code to use radeon_connector->edid
- add support for oem boards multiple connectors that share
a ddc line.
- short circuit lvds_detect() if have a stored edid
Signed-off-by: default avatarAlex Deucher <alexdeucher@gmail.com>
Signed-off-by: default avatarDave Airlie <airlied@redhat.com>
parent 1b4d7d75
...@@ -400,7 +400,6 @@ static enum drm_connector_status radeon_lvds_detect(struct drm_connector *connec ...@@ -400,7 +400,6 @@ static enum drm_connector_status radeon_lvds_detect(struct drm_connector *connec
struct radeon_connector *radeon_connector = to_radeon_connector(connector); struct radeon_connector *radeon_connector = to_radeon_connector(connector);
struct drm_encoder *encoder = radeon_best_single_encoder(connector); struct drm_encoder *encoder = radeon_best_single_encoder(connector);
enum drm_connector_status ret = connector_status_disconnected; enum drm_connector_status ret = connector_status_disconnected;
bool dret;
if (encoder) { if (encoder) {
struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
...@@ -413,12 +412,17 @@ static enum drm_connector_status radeon_lvds_detect(struct drm_connector *connec ...@@ -413,12 +412,17 @@ static enum drm_connector_status radeon_lvds_detect(struct drm_connector *connec
} }
/* check for edid as well */ /* check for edid as well */
if (radeon_connector->ddc_bus) { if (radeon_connector->edid)
radeon_i2c_do_lock(radeon_connector, 1); ret = connector_status_connected;
dret = radeon_ddc_probe(radeon_connector); else {
radeon_i2c_do_lock(radeon_connector, 0); if (radeon_connector->ddc_bus) {
if (dret) radeon_i2c_do_lock(radeon_connector, 1);
ret = connector_status_connected; radeon_connector->edid = drm_get_edid(&radeon_connector->base,
&radeon_connector->ddc_bus->adapter);
radeon_i2c_do_lock(radeon_connector, 0);
if (radeon_connector->edid)
ret = connector_status_connected;
}
} }
/* check acpi lid status ??? */ /* check acpi lid status ??? */
...@@ -432,6 +436,8 @@ static void radeon_connector_destroy(struct drm_connector *connector) ...@@ -432,6 +436,8 @@ static void radeon_connector_destroy(struct drm_connector *connector)
if (radeon_connector->ddc_bus) if (radeon_connector->ddc_bus)
radeon_i2c_destroy(radeon_connector->ddc_bus); radeon_i2c_destroy(radeon_connector->ddc_bus);
if (radeon_connector->edid)
kfree(radeon_connector->edid);
kfree(radeon_connector->con_priv); kfree(radeon_connector->con_priv);
drm_sysfs_connector_remove(connector); drm_sysfs_connector_remove(connector);
drm_connector_cleanup(connector); drm_connector_cleanup(connector);
...@@ -519,9 +525,32 @@ static enum drm_connector_status radeon_vga_detect(struct drm_connector *connect ...@@ -519,9 +525,32 @@ static enum drm_connector_status radeon_vga_detect(struct drm_connector *connect
radeon_i2c_do_lock(radeon_connector, 1); radeon_i2c_do_lock(radeon_connector, 1);
dret = radeon_ddc_probe(radeon_connector); dret = radeon_ddc_probe(radeon_connector);
radeon_i2c_do_lock(radeon_connector, 0); radeon_i2c_do_lock(radeon_connector, 0);
if (dret) if (dret) {
ret = connector_status_connected; if (radeon_connector->edid) {
else { kfree(radeon_connector->edid);
radeon_connector->edid = NULL;
}
radeon_i2c_do_lock(radeon_connector, 1);
radeon_connector->edid = drm_get_edid(&radeon_connector->base, &radeon_connector->ddc_bus->adapter);
radeon_i2c_do_lock(radeon_connector, 0);
if (!radeon_connector->edid) {
DRM_ERROR("DDC responded but not EDID found for %s\n",
drm_get_connector_name(connector));
} else {
radeon_connector->use_digital = !!(radeon_connector->edid->input & DRM_EDID_INPUT_DIGITAL);
/* some oems have boards with separate digital and analog connectors
* with a shared ddc line (often vga + hdmi)
*/
if (radeon_connector->use_digital && radeon_connector->shared_ddc) {
kfree(radeon_connector->edid);
radeon_connector->edid = NULL;
ret = connector_status_disconnected;
} else
ret = connector_status_connected;
}
} else {
if (radeon_connector->dac_load_detect) { if (radeon_connector->dac_load_detect) {
encoder_funcs = encoder->helper_private; encoder_funcs = encoder->helper_private;
ret = encoder_funcs->detect(encoder, connector); ret = encoder_funcs->detect(encoder, connector);
...@@ -649,6 +678,10 @@ static enum drm_connector_status radeon_dvi_detect(struct drm_connector *connect ...@@ -649,6 +678,10 @@ static enum drm_connector_status radeon_dvi_detect(struct drm_connector *connect
dret = radeon_ddc_probe(radeon_connector); dret = radeon_ddc_probe(radeon_connector);
radeon_i2c_do_lock(radeon_connector, 0); radeon_i2c_do_lock(radeon_connector, 0);
if (dret) { if (dret) {
if (radeon_connector->edid) {
kfree(radeon_connector->edid);
radeon_connector->edid = NULL;
}
radeon_i2c_do_lock(radeon_connector, 1); radeon_i2c_do_lock(radeon_connector, 1);
radeon_connector->edid = drm_get_edid(&radeon_connector->base, &radeon_connector->ddc_bus->adapter); radeon_connector->edid = drm_get_edid(&radeon_connector->base, &radeon_connector->ddc_bus->adapter);
radeon_i2c_do_lock(radeon_connector, 0); radeon_i2c_do_lock(radeon_connector, 0);
...@@ -659,10 +692,15 @@ static enum drm_connector_status radeon_dvi_detect(struct drm_connector *connect ...@@ -659,10 +692,15 @@ static enum drm_connector_status radeon_dvi_detect(struct drm_connector *connect
} else { } else {
radeon_connector->use_digital = !!(radeon_connector->edid->input & DRM_EDID_INPUT_DIGITAL); radeon_connector->use_digital = !!(radeon_connector->edid->input & DRM_EDID_INPUT_DIGITAL);
/* if this isn't a digital monitor /* some oems have boards with separate digital and analog connectors
then we need to make sure we don't have any * with a shared ddc line (often vga + hdmi)
TV conflicts */ */
ret = connector_status_connected; if ((!radeon_connector->use_digital) && radeon_connector->shared_ddc) {
kfree(radeon_connector->edid);
radeon_connector->edid = NULL;
ret = connector_status_disconnected;
} else
ret = connector_status_connected;
} }
} }
...@@ -787,6 +825,7 @@ radeon_add_atom_connector(struct drm_device *dev, ...@@ -787,6 +825,7 @@ radeon_add_atom_connector(struct drm_device *dev,
struct radeon_connector *radeon_connector; struct radeon_connector *radeon_connector;
struct radeon_connector_atom_dig *radeon_dig_connector; struct radeon_connector_atom_dig *radeon_dig_connector;
uint32_t subpixel_order = SubPixelNone; uint32_t subpixel_order = SubPixelNone;
bool shared_ddc = false;
int ret; int ret;
/* fixme - tv/cv/din */ /* fixme - tv/cv/din */
...@@ -800,6 +839,13 @@ radeon_add_atom_connector(struct drm_device *dev, ...@@ -800,6 +839,13 @@ radeon_add_atom_connector(struct drm_device *dev,
radeon_connector->devices |= supported_device; radeon_connector->devices |= supported_device;
return; return;
} }
if (radeon_connector->ddc_bus && i2c_bus->valid) {
if (memcmp(&radeon_connector->ddc_bus->rec, i2c_bus,
sizeof(struct radeon_i2c_bus_rec)) == 0) {
radeon_connector->shared_ddc = true;
shared_ddc = true;
}
}
} }
radeon_connector = kzalloc(sizeof(struct radeon_connector), GFP_KERNEL); radeon_connector = kzalloc(sizeof(struct radeon_connector), GFP_KERNEL);
...@@ -810,6 +856,7 @@ radeon_add_atom_connector(struct drm_device *dev, ...@@ -810,6 +856,7 @@ radeon_add_atom_connector(struct drm_device *dev,
radeon_connector->connector_id = connector_id; radeon_connector->connector_id = connector_id;
radeon_connector->devices = supported_device; radeon_connector->devices = supported_device;
radeon_connector->shared_ddc = shared_ddc;
switch (connector_type) { switch (connector_type) {
case DRM_MODE_CONNECTOR_VGA: case DRM_MODE_CONNECTOR_VGA:
drm_connector_init(dev, &radeon_connector->base, &radeon_vga_connector_funcs, connector_type); drm_connector_init(dev, &radeon_connector->base, &radeon_vga_connector_funcs, connector_type);
......
...@@ -334,27 +334,19 @@ static bool radeon_setup_enc_conn(struct drm_device *dev) ...@@ -334,27 +334,19 @@ static bool radeon_setup_enc_conn(struct drm_device *dev)
int radeon_ddc_get_modes(struct radeon_connector *radeon_connector) int radeon_ddc_get_modes(struct radeon_connector *radeon_connector)
{ {
struct edid *edid;
int ret = 0; int ret = 0;
if (!radeon_connector->ddc_bus) if (!radeon_connector->ddc_bus)
return -1; return -1;
if (!radeon_connector->edid) { if (!radeon_connector->edid) {
radeon_i2c_do_lock(radeon_connector, 1); radeon_i2c_do_lock(radeon_connector, 1);
edid = drm_get_edid(&radeon_connector->base, &radeon_connector->ddc_bus->adapter); radeon_connector->edid = drm_get_edid(&radeon_connector->base, &radeon_connector->ddc_bus->adapter);
radeon_i2c_do_lock(radeon_connector, 0); radeon_i2c_do_lock(radeon_connector, 0);
} else }
edid = radeon_connector->edid;
if (edid) { if (radeon_connector->edid) {
/* update digital bits here */ drm_mode_connector_update_edid_property(&radeon_connector->base, radeon_connector->edid);
if (edid->input & DRM_EDID_INPUT_DIGITAL) ret = drm_add_edid_modes(&radeon_connector->base, radeon_connector->edid);
radeon_connector->use_digital = 1;
else
radeon_connector->use_digital = 0;
drm_mode_connector_update_edid_property(&radeon_connector->base, edid);
ret = drm_add_edid_modes(&radeon_connector->base, edid);
kfree(edid);
return ret; return ret;
} }
drm_mode_connector_update_edid_property(&radeon_connector->base, NULL); drm_mode_connector_update_edid_property(&radeon_connector->base, NULL);
......
...@@ -449,7 +449,7 @@ atombios_digital_setup(struct drm_encoder *encoder, int action) ...@@ -449,7 +449,7 @@ atombios_digital_setup(struct drm_encoder *encoder, int action)
case 1: case 1:
args.v1.ucMisc = 0; args.v1.ucMisc = 0;
args.v1.ucAction = action; args.v1.ucAction = action;
if (drm_detect_hdmi_monitor((struct edid *)connector->edid_blob_ptr)) if (drm_detect_hdmi_monitor(radeon_connector->edid))
args.v1.ucMisc |= PANEL_ENCODER_MISC_HDMI_TYPE; args.v1.ucMisc |= PANEL_ENCODER_MISC_HDMI_TYPE;
args.v1.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10); args.v1.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10);
if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) { if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) {
...@@ -474,7 +474,7 @@ atombios_digital_setup(struct drm_encoder *encoder, int action) ...@@ -474,7 +474,7 @@ atombios_digital_setup(struct drm_encoder *encoder, int action)
if (dig->coherent_mode) if (dig->coherent_mode)
args.v2.ucMisc |= PANEL_ENCODER_MISC_COHERENT; args.v2.ucMisc |= PANEL_ENCODER_MISC_COHERENT;
} }
if (drm_detect_hdmi_monitor((struct edid *)connector->edid_blob_ptr)) if (drm_detect_hdmi_monitor(radeon_connector->edid))
args.v2.ucMisc |= PANEL_ENCODER_MISC_HDMI_TYPE; args.v2.ucMisc |= PANEL_ENCODER_MISC_HDMI_TYPE;
args.v2.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10); args.v2.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10);
args.v2.ucTruncate = 0; args.v2.ucTruncate = 0;
...@@ -532,7 +532,7 @@ atombios_get_encoder_mode(struct drm_encoder *encoder) ...@@ -532,7 +532,7 @@ atombios_get_encoder_mode(struct drm_encoder *encoder)
switch (connector->connector_type) { switch (connector->connector_type) {
case DRM_MODE_CONNECTOR_DVII: case DRM_MODE_CONNECTOR_DVII:
case DRM_MODE_CONNECTOR_HDMIB: /* HDMI-B is basically DL-DVI; analog works fine */ case DRM_MODE_CONNECTOR_HDMIB: /* HDMI-B is basically DL-DVI; analog works fine */
if (drm_detect_hdmi_monitor((struct edid *)connector->edid_blob_ptr)) if (drm_detect_hdmi_monitor(radeon_connector->edid))
return ATOM_ENCODER_MODE_HDMI; return ATOM_ENCODER_MODE_HDMI;
else if (radeon_connector->use_digital) else if (radeon_connector->use_digital)
return ATOM_ENCODER_MODE_DVI; return ATOM_ENCODER_MODE_DVI;
...@@ -542,7 +542,7 @@ atombios_get_encoder_mode(struct drm_encoder *encoder) ...@@ -542,7 +542,7 @@ atombios_get_encoder_mode(struct drm_encoder *encoder)
case DRM_MODE_CONNECTOR_DVID: case DRM_MODE_CONNECTOR_DVID:
case DRM_MODE_CONNECTOR_HDMIA: case DRM_MODE_CONNECTOR_HDMIA:
default: default:
if (drm_detect_hdmi_monitor((struct edid *)connector->edid_blob_ptr)) if (drm_detect_hdmi_monitor(radeon_connector->edid))
return ATOM_ENCODER_MODE_HDMI; return ATOM_ENCODER_MODE_HDMI;
else else
return ATOM_ENCODER_MODE_DVI; return ATOM_ENCODER_MODE_DVI;
...@@ -554,7 +554,7 @@ atombios_get_encoder_mode(struct drm_encoder *encoder) ...@@ -554,7 +554,7 @@ atombios_get_encoder_mode(struct drm_encoder *encoder)
/*if (radeon_output->MonType == MT_DP) /*if (radeon_output->MonType == MT_DP)
return ATOM_ENCODER_MODE_DP; return ATOM_ENCODER_MODE_DP;
else*/ else*/
if (drm_detect_hdmi_monitor((struct edid *)connector->edid_blob_ptr)) if (drm_detect_hdmi_monitor(radeon_connector->edid))
return ATOM_ENCODER_MODE_HDMI; return ATOM_ENCODER_MODE_HDMI;
else else
return ATOM_ENCODER_MODE_DVI; return ATOM_ENCODER_MODE_DVI;
......
...@@ -297,6 +297,8 @@ struct radeon_connector { ...@@ -297,6 +297,8 @@ struct radeon_connector {
uint32_t connector_id; uint32_t connector_id;
uint32_t devices; uint32_t devices;
struct radeon_i2c_chan *ddc_bus; struct radeon_i2c_chan *ddc_bus;
/* some systems have a an hdmi and vga port with a shared ddc line */
bool shared_ddc;
bool use_digital; bool use_digital;
/* we need to mind the EDID between detect /* we need to mind the EDID between detect
and get modes due to analog/digital/tvencoder */ and get modes due to analog/digital/tvencoder */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment