diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index d3b74ba62b4a..cabe32df7cd5 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -38,6 +38,18 @@ #undef SDVO_DEBUG #define I915_SDVO "i915_sdvo" +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; @@ -71,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. */ @@ -97,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 @@ -945,23 +958,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_priv *sdvo_priv = output->dev_priv; - struct intel_sdvo_tv_format *format, unset; - u8 status; - format = &sdvo_priv->tv_format; - memset(&unset, 0, sizeof(unset)); - if (memcmp(format, &unset, sizeof(*format))) { - DRM_DEBUG("%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("%s: Failed to set TV format\n", - SDVO_NAME(sdvo_priv)); - } + struct intel_sdvo_tv_format format; + struct intel_sdvo_priv *sdvo_priv = output->dev_priv; + uint32_t format_map, i; + uint8_t status; + + 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, @@ -1516,7 +1534,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("SDVO response %d %d\n", response & 0xff, response >> 8); @@ -1567,25 +1586,6 @@ static void intel_sdvo_get_ddc_modes(struct drm_connector *connector) #endif } -/** - * 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; - - 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; - - memcpy(&dev_priv->tv_format, &format, sizeof(format)); -} - /* * Set of SDVO TV modes. * Note! This is in reply order (see loop in get_tv_modes). @@ -1656,17 +1656,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); @@ -1681,6 +1690,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) @@ -1753,12 +1763,55 @@ static void intel_sdvo_destroy(struct drm_connector *connector) 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, @@ -1773,6 +1826,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, }; @@ -2029,6 +2083,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; @@ -2111,6 +2214,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);