drm/i915: Enable PAL and SECAM format and add the propery for SDVO-TV
authorZhao Yakui <yakui.zhao@intel.com>
Mon, 24 Aug 2009 05:50:26 +0000 (13:50 +0800)
committerEric Anholt <eric@anholt.net>
Fri, 4 Sep 2009 20:05:11 +0000 (13:05 -0700)
Currently SDVO TV only support NTSC-M format. In this patch
we introduce PAL and SECAM formats available and create seting-format
property at init time. When user dynamically chose preferred
format by xrandr command, it will refine all modelines
provided by SDVO device, then instruct SDVO device to execute.
At the same time the property is added for SDVO-TV so that the SDVO-TV mode can be changed
by using xrandr.

https://bugs.freedesktop.org/show_bug.cgi?id=22891

Signed-off-by: Ma Ling <ling.ma@intel.com>
review-by: Zhao Yakui <yakui.zhao@intel.com>
Signed-off-by: Eric Anholt <eric@anholt.net>
drivers/gpu/drm/i915/intel_sdvo.c

index d3b74ba..cabe32d 100644 (file)
 
 #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_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("%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));
-       }
+       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);