diff options
author | Dave Airlie <airlied@redhat.com> | 2009-09-07 20:27:20 +1000 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2009-09-07 20:27:20 +1000 |
commit | 11670d3c93210793562748d83502ecbef4034765 (patch) | |
tree | 9e2c33c6249e26b05a2b5db87d4f4840e9049840 /drivers/gpu/drm/i915/intel_sdvo.c | |
parent | 575dc34ee0de867ba83abf25998e0963bff451fa (diff) | |
parent | 01dfba93d9dfcf6d7abfc55ff5d9d6e76fa01ba0 (diff) |
Merge intel drm-intel-next branch
Merge remote branch 'anholt/drm-intel-next' of ../anholt-2.6 into drm-next
Conflicts:
drivers/gpu/drm/i915/intel_display.c
drivers/gpu/drm/i915/intel_drv.h
drivers/gpu/drm/i915/intel_sdvo.c
Diffstat (limited to 'drivers/gpu/drm/i915/intel_sdvo.c')
-rw-r--r-- | drivers/gpu/drm/i915/intel_sdvo.c | 307 |
1 files changed, 238 insertions, 69 deletions
diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index 91238f0c39a..0bf28efcf2c 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -37,6 +37,19 @@ #include "intel_sdvo_regs.h" #undef SDVO_DEBUG + +static char *tv_format_names[] = { + "NTSC_M" , "NTSC_J" , "NTSC_443", + "PAL_B" , "PAL_D" , "PAL_G" , + "PAL_H" , "PAL_I" , "PAL_M" , + "PAL_N" , "PAL_NC" , "PAL_60" , + "SECAM_B" , "SECAM_D" , "SECAM_G" , + "SECAM_K" , "SECAM_K1", "SECAM_L" , + "SECAM_60" +}; + +#define TV_FORMAT_NUM (sizeof(tv_format_names) / sizeof(*tv_format_names)) + struct intel_sdvo_priv { u8 slave_addr; @@ -70,6 +83,15 @@ struct intel_sdvo_priv { */ bool is_tv; + /* This is for current tv format name */ + char *tv_format_name; + + /* This contains all current supported TV format */ + char *tv_format_supported[TV_FORMAT_NUM]; + int format_supported_num; + struct drm_property *tv_format_property; + struct drm_property *tv_format_name_property[TV_FORMAT_NUM]; + /** * This is set if we treat the device as HDMI, instead of DVI. */ @@ -96,14 +118,6 @@ struct intel_sdvo_priv { */ struct intel_sdvo_sdtv_resolution_reply sdtv_resolutions; - /** - * Current selected TV format. - * - * This is stored in the same structure that's passed to the device, for - * convenience. - */ - struct intel_sdvo_tv_format tv_format; - /* * supported encoding mode, used to determine whether HDMI is * supported @@ -113,6 +127,9 @@ struct intel_sdvo_priv { /* DDC bus used by this SDVO output */ uint8_t ddc_bus; + /* Mac mini hack -- use the same DDC as the analog connector */ + struct i2c_adapter *analog_ddc_bus; + int save_sdvo_mult; u16 save_active_outputs; struct intel_sdvo_dtd save_input_dtd_1, save_input_dtd_2; @@ -944,23 +961,28 @@ static void intel_sdvo_set_avi_infoframe(struct intel_output *output, static void intel_sdvo_set_tv_format(struct intel_output *output) { + + struct intel_sdvo_tv_format format; struct intel_sdvo_priv *sdvo_priv = output->dev_priv; - struct intel_sdvo_tv_format *format, unset; - u8 status; + uint32_t format_map, i; + uint8_t status; - format = &sdvo_priv->tv_format; - memset(&unset, 0, sizeof(unset)); - if (memcmp(format, &unset, sizeof(*format))) { - DRM_DEBUG_KMS("%s: Choosing default TV format of NTSC-M\n", - SDVO_NAME(sdvo_priv)); - format->ntsc_m = 1; - intel_sdvo_write_cmd(output, SDVO_CMD_SET_TV_FORMAT, format, - sizeof(*format)); - status = intel_sdvo_read_response(output, NULL, 0); - if (status != SDVO_CMD_STATUS_SUCCESS) - DRM_DEBUG_KMS("%s: Failed to set TV format\n", - SDVO_NAME(sdvo_priv)); - } + for (i = 0; i < TV_FORMAT_NUM; i++) + if (tv_format_names[i] == sdvo_priv->tv_format_name) + break; + + format_map = 1 << i; + memset(&format, 0, sizeof(format)); + memcpy(&format, &format_map, sizeof(format_map) > sizeof(format) ? + sizeof(format) : sizeof(format_map)); + + intel_sdvo_write_cmd(output, SDVO_CMD_SET_TV_FORMAT, &format_map, + sizeof(format)); + + status = intel_sdvo_read_response(output, NULL, 0); + if (status != SDVO_CMD_STATUS_SUCCESS) + DRM_DEBUG("%s: Failed to set TV format\n", + SDVO_NAME(sdvo_priv)); } static bool intel_sdvo_mode_fixup(struct drm_encoder *encoder, @@ -1457,7 +1479,7 @@ intel_sdvo_multifunc_encoder(struct intel_output *intel_output) (SDVO_OUTPUT_RGB0 | SDVO_OUTPUT_RGB1)) caps++; if (sdvo_priv->caps.output_flags & - (SDVO_OUTPUT_SVID0 | SDVO_OUTPUT_SVID0)) + (SDVO_OUTPUT_SVID0 | SDVO_OUTPUT_SVID1)) caps++; if (sdvo_priv->caps.output_flags & (SDVO_OUTPUT_CVBS0 | SDVO_OUTPUT_CVBS1)) @@ -1477,6 +1499,36 @@ intel_sdvo_multifunc_encoder(struct intel_output *intel_output) return (caps > 1); } +static struct drm_connector * +intel_find_analog_connector(struct drm_device *dev) +{ + struct drm_connector *connector; + struct intel_output *intel_output; + + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + intel_output = to_intel_output(connector); + if (intel_output->type == INTEL_OUTPUT_ANALOG) + return connector; + } + return NULL; +} + +static int +intel_analog_is_connected(struct drm_device *dev) +{ + struct drm_connector *analog_connector; + analog_connector = intel_find_analog_connector(dev); + + if (!analog_connector) + return false; + + if (analog_connector->funcs->detect(analog_connector) == + connector_status_disconnected) + return false; + + return true; +} + enum drm_connector_status intel_sdvo_hdmi_sink_detect(struct drm_connector *connector, u16 response) { @@ -1487,6 +1539,15 @@ intel_sdvo_hdmi_sink_detect(struct drm_connector *connector, u16 response) edid = drm_get_edid(&intel_output->base, intel_output->ddc_bus); + + /* when there is no edid and no monitor is connected with VGA + * port, try to use the CRT ddc to read the EDID for DVI-connector + */ + if (edid == NULL && + sdvo_priv->analog_ddc_bus && + !intel_analog_is_connected(intel_output->base.dev)) + edid = drm_get_edid(&intel_output->base, + sdvo_priv->analog_ddc_bus); if (edid != NULL) { /* Don't report the output as connected if it's a DVI-I * connector with a non-digital EDID coming out. @@ -1515,7 +1576,8 @@ static enum drm_connector_status intel_sdvo_detect(struct drm_connector *connect struct intel_output *intel_output = to_intel_output(connector); struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv; - intel_sdvo_write_cmd(intel_output, SDVO_CMD_GET_ATTACHED_DISPLAYS, NULL, 0); + intel_sdvo_write_cmd(intel_output, + SDVO_CMD_GET_ATTACHED_DISPLAYS, NULL, 0); status = intel_sdvo_read_response(intel_output, &response, 2); DRM_DEBUG_KMS("SDVO response %d %d\n", response & 0xff, response >> 8); @@ -1539,50 +1601,32 @@ static enum drm_connector_status intel_sdvo_detect(struct drm_connector *connect static void intel_sdvo_get_ddc_modes(struct drm_connector *connector) { struct intel_output *intel_output = to_intel_output(connector); + struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv; + int num_modes; /* set the bus switch and get the modes */ - intel_ddc_get_modes(intel_output); + num_modes = intel_ddc_get_modes(intel_output); -#if 0 - struct drm_device *dev = encoder->dev; - struct drm_i915_private *dev_priv = dev->dev_private; - /* Mac mini hack. On this device, I get DDC through the analog, which - * load-detects as disconnected. I fail to DDC through the SDVO DDC, - * but it does load-detect as connected. So, just steal the DDC bits - * from analog when we fail at finding it the right way. + /* + * Mac mini hack. On this device, the DVI-I connector shares one DDC + * link between analog and digital outputs. So, if the regular SDVO + * DDC fails, check to see if the analog output is disconnected, in + * which case we'll look there for the digital DDC data. */ - crt = xf86_config->output[0]; - intel_output = crt->driver_private; - if (intel_output->type == I830_OUTPUT_ANALOG && - crt->funcs->detect(crt) == XF86OutputStatusDisconnected) { - I830I2CInit(pScrn, &intel_output->pDDCBus, GPIOA, "CRTDDC_A"); - edid_mon = xf86OutputGetEDID(crt, intel_output->pDDCBus); - xf86DestroyI2CBusRec(intel_output->pDDCBus, true, true); - } - if (edid_mon) { - xf86OutputSetEDID(output, edid_mon); - modes = xf86OutputGetEDIDModes(output); - } -#endif -} + if (num_modes == 0 && + sdvo_priv->analog_ddc_bus && + !intel_analog_is_connected(intel_output->base.dev)) { + struct i2c_adapter *digital_ddc_bus; -/** - * This function checks the current TV format, and chooses a default if - * it hasn't been set. - */ -static void -intel_sdvo_check_tv_format(struct intel_output *output) -{ - struct intel_sdvo_priv *dev_priv = output->dev_priv; - struct intel_sdvo_tv_format format; - uint8_t status; + /* Switch to the analog ddc bus and try that + */ + digital_ddc_bus = intel_output->ddc_bus; + intel_output->ddc_bus = sdvo_priv->analog_ddc_bus; - intel_sdvo_write_cmd(output, SDVO_CMD_GET_TV_FORMAT, NULL, 0); - status = intel_sdvo_read_response(output, &format, sizeof(format)); - if (status != SDVO_CMD_STATUS_SUCCESS) - return; + (void) intel_ddc_get_modes(intel_output); - memcpy(&dev_priv->tv_format, &format, sizeof(format)); + intel_output->ddc_bus = digital_ddc_bus; + } } /* @@ -1655,17 +1699,26 @@ static void intel_sdvo_get_tv_modes(struct drm_connector *connector) struct intel_output *output = to_intel_output(connector); struct intel_sdvo_priv *sdvo_priv = output->dev_priv; struct intel_sdvo_sdtv_resolution_request tv_res; - uint32_t reply = 0; + uint32_t reply = 0, format_map = 0; + int i; uint8_t status; - int i = 0; - intel_sdvo_check_tv_format(output); /* Read the list of supported input resolutions for the selected TV * format. */ - memset(&tv_res, 0, sizeof(tv_res)); - memcpy(&tv_res, &sdvo_priv->tv_format, sizeof(tv_res)); + for (i = 0; i < TV_FORMAT_NUM; i++) + if (tv_format_names[i] == sdvo_priv->tv_format_name) + break; + + format_map = (1 << i); + memcpy(&tv_res, &format_map, + sizeof(struct intel_sdvo_sdtv_resolution_request) > + sizeof(format_map) ? sizeof(format_map) : + sizeof(struct intel_sdvo_sdtv_resolution_request)); + + intel_sdvo_set_target_output(output, sdvo_priv->controlled_output); + intel_sdvo_write_cmd(output, SDVO_CMD_GET_SDTV_RESOLUTION_SUPPORT, &tv_res, sizeof(tv_res)); status = intel_sdvo_read_response(output, &reply, 3); @@ -1680,6 +1733,7 @@ static void intel_sdvo_get_tv_modes(struct drm_connector *connector) if (nmode) drm_mode_probed_add(connector, nmode); } + } static void intel_sdvo_get_lvds_modes(struct drm_connector *connector) @@ -1747,17 +1801,62 @@ static void intel_sdvo_destroy(struct drm_connector *connector) intel_i2c_destroy(intel_output->i2c_bus); if (intel_output->ddc_bus) intel_i2c_destroy(intel_output->ddc_bus); + if (sdvo_priv->analog_ddc_bus) + intel_i2c_destroy(sdvo_priv->analog_ddc_bus); if (sdvo_priv->sdvo_lvds_fixed_mode != NULL) drm_mode_destroy(connector->dev, sdvo_priv->sdvo_lvds_fixed_mode); + if (sdvo_priv->tv_format_property) + drm_property_destroy(connector->dev, + sdvo_priv->tv_format_property); + drm_sysfs_connector_remove(connector); drm_connector_cleanup(connector); kfree(intel_output); } +static int +intel_sdvo_set_property(struct drm_connector *connector, + struct drm_property *property, + uint64_t val) +{ + struct intel_output *intel_output = to_intel_output(connector); + struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv; + struct drm_encoder *encoder = &intel_output->enc; + struct drm_crtc *crtc = encoder->crtc; + int ret = 0; + bool changed = false; + + ret = drm_connector_property_set_value(connector, property, val); + if (ret < 0) + goto out; + + if (property == sdvo_priv->tv_format_property) { + if (val >= TV_FORMAT_NUM) { + ret = -EINVAL; + goto out; + } + if (sdvo_priv->tv_format_name == + sdvo_priv->tv_format_supported[val]) + goto out; + + sdvo_priv->tv_format_name = sdvo_priv->tv_format_supported[val]; + changed = true; + } else { + ret = -EINVAL; + goto out; + } + + if (changed && crtc) + drm_crtc_helper_set_mode(crtc, &crtc->mode, crtc->x, + crtc->y, crtc->fb); +out: + return ret; +} + static const struct drm_encoder_helper_funcs intel_sdvo_helper_funcs = { .dpms = intel_sdvo_dpms, .mode_fixup = intel_sdvo_mode_fixup, @@ -1772,6 +1871,7 @@ static const struct drm_connector_funcs intel_sdvo_connector_funcs = { .restore = intel_sdvo_restore, .detect = intel_sdvo_detect, .fill_modes = drm_helper_probe_single_connector_modes, + .set_property = intel_sdvo_set_property, .destroy = intel_sdvo_destroy, }; @@ -1966,6 +2066,9 @@ intel_sdvo_output_setup(struct intel_output *intel_output, uint16_t flags) intel_sdvo_set_colorimetry(intel_output, SDVO_COLORIMETRY_RGB256); connector->connector_type = DRM_MODE_CONNECTOR_HDMIA; + intel_output->clone_mask = + (1 << INTEL_SDVO_NON_TV_CLONE_BIT) | + (1 << INTEL_ANALOG_CLONE_BIT); } } else if (flags & SDVO_OUTPUT_SVID0) { @@ -1974,11 +2077,14 @@ intel_sdvo_output_setup(struct intel_output *intel_output, uint16_t flags) connector->connector_type = DRM_MODE_CONNECTOR_SVIDEO; sdvo_priv->is_tv = true; intel_output->needs_tv_clock = true; + intel_output->clone_mask = 1 << INTEL_SDVO_TV_CLONE_BIT; } else if (flags & SDVO_OUTPUT_RGB0) { sdvo_priv->controlled_output = SDVO_OUTPUT_RGB0; encoder->encoder_type = DRM_MODE_ENCODER_DAC; connector->connector_type = DRM_MODE_CONNECTOR_VGA; + intel_output->clone_mask = (1 << INTEL_SDVO_NON_TV_CLONE_BIT) | + (1 << INTEL_ANALOG_CLONE_BIT); } else if (flags & SDVO_OUTPUT_RGB1) { sdvo_priv->controlled_output = SDVO_OUTPUT_RGB1; @@ -1990,12 +2096,16 @@ intel_sdvo_output_setup(struct intel_output *intel_output, uint16_t flags) encoder->encoder_type = DRM_MODE_ENCODER_LVDS; connector->connector_type = DRM_MODE_CONNECTOR_LVDS; sdvo_priv->is_lvds = true; + intel_output->clone_mask = (1 << INTEL_ANALOG_CLONE_BIT) | + (1 << INTEL_SDVO_LVDS_CLONE_BIT); } else if (flags & SDVO_OUTPUT_LVDS1) { sdvo_priv->controlled_output = SDVO_OUTPUT_LVDS1; encoder->encoder_type = DRM_MODE_ENCODER_LVDS; connector->connector_type = DRM_MODE_CONNECTOR_LVDS; sdvo_priv->is_lvds = true; + intel_output->clone_mask = (1 << INTEL_ANALOG_CLONE_BIT) | + (1 << INTEL_SDVO_LVDS_CLONE_BIT); } else { unsigned char bytes[2]; @@ -2007,6 +2117,7 @@ intel_sdvo_output_setup(struct intel_output *intel_output, uint16_t flags) bytes[0], bytes[1]); ret = false; } + intel_output->crtc_mask = (1 << 0) | (1 << 1); if (ret && registered) ret = drm_sysfs_connector_add(connector) == 0 ? true : false; @@ -2016,6 +2127,55 @@ intel_sdvo_output_setup(struct intel_output *intel_output, uint16_t flags) } +static void intel_sdvo_tv_create_property(struct drm_connector *connector) +{ + struct intel_output *intel_output = to_intel_output(connector); + struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv; + struct intel_sdvo_tv_format format; + uint32_t format_map, i; + uint8_t status; + + intel_sdvo_set_target_output(intel_output, + sdvo_priv->controlled_output); + + intel_sdvo_write_cmd(intel_output, + SDVO_CMD_GET_SUPPORTED_TV_FORMATS, NULL, 0); + status = intel_sdvo_read_response(intel_output, + &format, sizeof(format)); + if (status != SDVO_CMD_STATUS_SUCCESS) + return; + + memcpy(&format_map, &format, sizeof(format) > sizeof(format_map) ? + sizeof(format_map) : sizeof(format)); + + if (format_map == 0) + return; + + sdvo_priv->format_supported_num = 0; + for (i = 0 ; i < TV_FORMAT_NUM; i++) + if (format_map & (1 << i)) { + sdvo_priv->tv_format_supported + [sdvo_priv->format_supported_num++] = + tv_format_names[i]; + } + + + sdvo_priv->tv_format_property = + drm_property_create( + connector->dev, DRM_MODE_PROP_ENUM, + "mode", sdvo_priv->format_supported_num); + + for (i = 0; i < sdvo_priv->format_supported_num; i++) + drm_property_add_enum( + sdvo_priv->tv_format_property, i, + i, sdvo_priv->tv_format_supported[i]); + + sdvo_priv->tv_format_name = sdvo_priv->tv_format_supported[0]; + drm_connector_attach_property( + connector, sdvo_priv->tv_format_property, 0); + +} + bool intel_sdvo_init(struct drm_device *dev, int output_device) { struct drm_connector *connector; @@ -2060,10 +2220,15 @@ bool intel_sdvo_init(struct drm_device *dev, int output_device) } /* setup the DDC bus. */ - if (output_device == SDVOB) + if (output_device == SDVOB) { intel_output->ddc_bus = intel_i2c_create(dev, GPIOE, "SDVOB DDC BUS"); - else + sdvo_priv->analog_ddc_bus = intel_i2c_create(dev, GPIOA, + "SDVOB/VGA DDC BUS"); + } else { intel_output->ddc_bus = intel_i2c_create(dev, GPIOE, "SDVOC DDC BUS"); + sdvo_priv->analog_ddc_bus = intel_i2c_create(dev, GPIOA, + "SDVOC/VGA DDC BUS"); + } if (intel_output->ddc_bus == NULL) goto err_i2c; @@ -2097,6 +2262,8 @@ bool intel_sdvo_init(struct drm_device *dev, int output_device) drm_encoder_helper_add(&intel_output->enc, &intel_sdvo_helper_funcs); drm_mode_connector_attach_encoder(&intel_output->base, &intel_output->enc); + if (sdvo_priv->is_tv) + intel_sdvo_tv_create_property(connector); drm_sysfs_connector_add(connector); intel_sdvo_select_ddc_bus(sdvo_priv); @@ -2129,6 +2296,8 @@ bool intel_sdvo_init(struct drm_device *dev, int output_device) return true; err_i2c: + if (sdvo_priv->analog_ddc_bus != NULL) + intel_i2c_destroy(sdvo_priv->analog_ddc_bus); if (intel_output->ddc_bus != NULL) intel_i2c_destroy(intel_output->ddc_bus); if (intel_output->i2c_bus != NULL) |