include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit...
[safe/jmp/linux-2.6] / drivers / gpu / drm / i915 / intel_sdvo.c
index c585da8..26e13a0 100644 (file)
@@ -26,6 +26,7 @@
  *     Eric Anholt <eric@anholt.net>
  */
 #include <linux/i2c.h>
+#include <linux/slab.h>
 #include <linux/delay.h>
 #include "drmP.h"
 #include "drm.h"
@@ -35,9 +36,8 @@
 #include "i915_drm.h"
 #include "i915_drv.h"
 #include "intel_sdvo_regs.h"
+#include <linux/dmi.h>
 
-#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"   ,
@@ -135,6 +135,30 @@ struct intel_sdvo_priv {
        struct intel_sdvo_dtd save_input_dtd_1, save_input_dtd_2;
        struct intel_sdvo_dtd save_output_dtd[16];
        u32 save_SDVOX;
+       /* add the property for the SDVO-TV */
+       struct drm_property *left_property;
+       struct drm_property *right_property;
+       struct drm_property *top_property;
+       struct drm_property *bottom_property;
+       struct drm_property *hpos_property;
+       struct drm_property *vpos_property;
+
+       /* add the property for the SDVO-TV/LVDS */
+       struct drm_property *brightness_property;
+       struct drm_property *contrast_property;
+       struct drm_property *saturation_property;
+       struct drm_property *hue_property;
+
+       /* Add variable to record current setting for the above property */
+       u32     left_margin, right_margin, top_margin, bottom_margin;
+       /* this is to get the range of margin.*/
+       u32     max_hscan,  max_vscan;
+       u32     max_hpos, cur_hpos;
+       u32     max_vpos, cur_vpos;
+       u32     cur_brightness, max_brightness;
+       u32     cur_contrast,   max_contrast;
+       u32     cur_saturation, max_saturation;
+       u32     cur_hue,        max_hue;
 };
 
 static bool
@@ -204,7 +228,7 @@ static bool intel_sdvo_read_byte(struct intel_output *intel_output, u8 addr,
                return true;
        }
 
-       DRM_DEBUG("i2c transfer returned %d\n", ret);
+       DRM_DEBUG_KMS("i2c transfer returned %d\n", ret);
        return false;
 }
 
@@ -281,6 +305,31 @@ static const struct _sdvo_cmd_name {
     SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SDTV_RESOLUTION_SUPPORT),
     SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SCALED_HDTV_RESOLUTION_SUPPORT),
     SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_ENHANCEMENTS),
+    /* Add the op code for SDVO enhancements */
+    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_POSITION_H),
+    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_POSITION_H),
+    SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_POSITION_H),
+    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_POSITION_V),
+    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_POSITION_V),
+    SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_POSITION_V),
+    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_SATURATION),
+    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SATURATION),
+    SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_SATURATION),
+    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_HUE),
+    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HUE),
+    SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HUE),
+    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_CONTRAST),
+    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_CONTRAST),
+    SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_CONTRAST),
+    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_BRIGHTNESS),
+    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_BRIGHTNESS),
+    SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_BRIGHTNESS),
+    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_OVERSCAN_H),
+    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OVERSCAN_H),
+    SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_OVERSCAN_H),
+    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_OVERSCAN_V),
+    SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OVERSCAN_V),
+    SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_OVERSCAN_V),
     /* HDMI op code */
     SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPP_ENCODE),
     SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ENCODE),
@@ -307,14 +356,13 @@ static const struct _sdvo_cmd_name {
 #define SDVO_NAME(dev_priv) ((dev_priv)->output_device == SDVOB ? "SDVOB" : "SDVOC")
 #define SDVO_PRIV(output)   ((struct intel_sdvo_priv *) (output)->dev_priv)
 
-#ifdef SDVO_DEBUG
 static void intel_sdvo_debug_write(struct intel_output *intel_output, u8 cmd,
                                   void *args, int args_len)
 {
        struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
        int i;
 
-       DRM_DEBUG_KMS(I915_SDVO, "%s: W: %02X ",
+       DRM_DEBUG_KMS("%s: W: %02X ",
                                SDVO_NAME(sdvo_priv), cmd);
        for (i = 0; i < args_len; i++)
                DRM_LOG_KMS("%02X ", ((u8 *)args)[i]);
@@ -330,9 +378,6 @@ static void intel_sdvo_debug_write(struct intel_output *intel_output, u8 cmd,
                DRM_LOG_KMS("(%02X)", cmd);
        DRM_LOG_KMS("\n");
 }
-#else
-#define intel_sdvo_debug_write(o, c, a, l)
-#endif
 
 static void intel_sdvo_write_cmd(struct intel_output *intel_output, u8 cmd,
                                 void *args, int args_len)
@@ -349,7 +394,6 @@ static void intel_sdvo_write_cmd(struct intel_output *intel_output, u8 cmd,
        intel_sdvo_write_byte(intel_output, SDVO_I2C_OPCODE, cmd);
 }
 
-#ifdef SDVO_DEBUG
 static const char *cmd_status_names[] = {
        "Power on",
        "Success",
@@ -367,7 +411,7 @@ static void intel_sdvo_debug_response(struct intel_output *intel_output,
        struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
        int i;
 
-       DRM_DEBUG_KMS(I915_SDVO, "%s: R: ", SDVO_NAME(sdvo_priv));
+       DRM_DEBUG_KMS("%s: R: ", SDVO_NAME(sdvo_priv));
        for (i = 0; i < response_len; i++)
                DRM_LOG_KMS("%02X ", ((u8 *)response)[i]);
        for (; i < 8; i++)
@@ -378,9 +422,6 @@ static void intel_sdvo_debug_response(struct intel_output *intel_output,
                DRM_LOG_KMS("(??? %d)", status);
        DRM_LOG_KMS("\n");
 }
-#else
-#define intel_sdvo_debug_response(o, r, l, s)
-#endif
 
 static u8 intel_sdvo_read_response(struct intel_output *intel_output,
                                   void *response, int response_len)
@@ -423,14 +464,63 @@ static int intel_sdvo_get_pixel_multiplier(struct drm_display_mode *mode)
 }
 
 /**
- * Don't check status code from this as it switches the bus back to the
- * SDVO chips which defeats the purpose of doing a bus switch in the first
- * place.
+ * Try to read the response after issuie the DDC switch command. But it
+ * is noted that we must do the action of reading response and issuing DDC
+ * switch command in one I2C transaction. Otherwise when we try to start
+ * another I2C transaction after issuing the DDC bus switch, it will be
+ * switched to the internal SDVO register.
  */
 static void intel_sdvo_set_control_bus_switch(struct intel_output *intel_output,
                                              u8 target)
 {
-       intel_sdvo_write_cmd(intel_output, SDVO_CMD_SET_CONTROL_BUS_SWITCH, &target, 1);
+       struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
+       u8 out_buf[2], cmd_buf[2], ret_value[2], ret;
+       struct i2c_msg msgs[] = {
+               {
+                       .addr = sdvo_priv->slave_addr >> 1,
+                       .flags = 0,
+                       .len = 2,
+                       .buf = out_buf,
+               },
+               /* the following two are to read the response */
+               {
+                       .addr = sdvo_priv->slave_addr >> 1,
+                       .flags = 0,
+                       .len = 1,
+                       .buf = cmd_buf,
+               },
+               {
+                       .addr = sdvo_priv->slave_addr >> 1,
+                       .flags = I2C_M_RD,
+                       .len = 1,
+                       .buf = ret_value,
+               },
+       };
+
+       intel_sdvo_debug_write(intel_output, SDVO_CMD_SET_CONTROL_BUS_SWITCH,
+                                       &target, 1);
+       /* write the DDC switch command argument */
+       intel_sdvo_write_byte(intel_output, SDVO_I2C_ARG_0, target);
+
+       out_buf[0] = SDVO_I2C_OPCODE;
+       out_buf[1] = SDVO_CMD_SET_CONTROL_BUS_SWITCH;
+       cmd_buf[0] = SDVO_I2C_CMD_STATUS;
+       cmd_buf[1] = 0;
+       ret_value[0] = 0;
+       ret_value[1] = 0;
+
+       ret = i2c_transfer(intel_output->i2c_bus, msgs, 3);
+       if (ret != 3) {
+               /* failure in I2C transfer */
+               DRM_DEBUG_KMS("I2c transfer returned %d\n", ret);
+               return;
+       }
+       if (ret_value[0] != SDVO_CMD_STATUS_SUCCESS) {
+               DRM_DEBUG_KMS("DDC switch command returns response %d\n",
+                                       ret_value[0]);
+               return;
+       }
+       return;
 }
 
 static bool intel_sdvo_set_target_input(struct intel_output *intel_output, bool target_0, bool target_1)
@@ -684,10 +774,10 @@ static int intel_sdvo_get_clock_rate_mult(struct intel_output *intel_output)
        status = intel_sdvo_read_response(intel_output, &response, 1);
 
        if (status != SDVO_CMD_STATUS_SUCCESS) {
-               DRM_DEBUG("Couldn't get SDVO clock rate multiplier\n");
+               DRM_DEBUG_KMS("Couldn't get SDVO clock rate multiplier\n");
                return SDVO_CLOCK_RATE_MULT_1X;
        } else {
-               DRM_DEBUG("Current clock rate multiplier: %d\n", response);
+               DRM_DEBUG_KMS("Current clock rate multiplier: %d\n", response);
        }
 
        return response;
@@ -981,7 +1071,7 @@ static void intel_sdvo_set_tv_format(struct intel_output *output)
 
        status = intel_sdvo_read_response(output, NULL, 0);
        if (status != SDVO_CMD_STATUS_SUCCESS)
-               DRM_DEBUG("%s: Failed to set TV format\n",
+               DRM_DEBUG_KMS("%s: Failed to set TV format\n",
                          SDVO_NAME(sdvo_priv));
 }
 
@@ -1251,8 +1341,8 @@ static void intel_sdvo_dpms(struct drm_encoder *encoder, int mode)
                 * a given it the status is a success, we succeeded.
                 */
                if (status == SDVO_CMD_STATUS_SUCCESS && !input1) {
-                       DRM_DEBUG("First %s output reported failure to sync\n",
-                                  SDVO_NAME(sdvo_priv));
+                       DRM_DEBUG_KMS("First %s output reported failure to "
+                                       "sync\n", SDVO_NAME(sdvo_priv));
                }
 
                if (0)
@@ -1347,8 +1437,8 @@ static void intel_sdvo_restore(struct drm_connector *connector)
                        intel_wait_for_vblank(dev);
                status = intel_sdvo_get_trained_inputs(intel_output, &input1, &input2);
                if (status == SDVO_CMD_STATUS_SUCCESS && !input1)
-                       DRM_DEBUG("First %s output reported failure to sync\n",
-                                  SDVO_NAME(sdvo_priv));
+                       DRM_DEBUG_KMS("First %s output reported failure to "
+                                       "sync\n", SDVO_NAME(sdvo_priv));
        }
 
        intel_sdvo_set_active_outputs(intel_output, sdvo_priv->save_active_outputs);
@@ -1426,7 +1516,7 @@ int intel_sdvo_supports_hotplug(struct drm_connector *connector)
        u8 response[2];
        u8 status;
        struct intel_output *intel_output;
-       DRM_DEBUG("\n");
+       DRM_DEBUG_KMS("\n");
 
        if (!connector)
                return 0;
@@ -1540,6 +1630,32 @@ intel_sdvo_hdmi_sink_detect(struct drm_connector *connector, u16 response)
        edid = drm_get_edid(&intel_output->base,
                            intel_output->ddc_bus);
 
+       /* This is only applied to SDVO cards with multiple outputs */
+       if (edid == NULL && intel_sdvo_multifunc_encoder(intel_output)) {
+               uint8_t saved_ddc, temp_ddc;
+               saved_ddc = sdvo_priv->ddc_bus;
+               temp_ddc = sdvo_priv->ddc_bus >> 1;
+               /*
+                * Don't use the 1 as the argument of DDC bus switch to get
+                * the EDID. It is used for SDVO SPD ROM.
+                */
+               while(temp_ddc > 1) {
+                       sdvo_priv->ddc_bus = temp_ddc;
+                       edid = drm_get_edid(&intel_output->base,
+                               intel_output->ddc_bus);
+                       if (edid) {
+                               /*
+                                * When we can get the EDID, maybe it is the
+                                * correct DDC bus. Update it.
+                                */
+                               sdvo_priv->ddc_bus = temp_ddc;
+                               break;
+                       }
+                       temp_ddc >>= 1;
+               }
+               if (edid == NULL)
+                       sdvo_priv->ddc_bus = saved_ddc;
+       }
        /* 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
         */
@@ -1578,9 +1694,13 @@ static enum drm_connector_status intel_sdvo_detect(struct drm_connector *connect
 
        intel_sdvo_write_cmd(intel_output,
                             SDVO_CMD_GET_ATTACHED_DISPLAYS, NULL, 0);
+       if (sdvo_priv->is_tv) {
+               /* add 30ms delay when the output type is SDVO-TV */
+               mdelay(30);
+       }
        status = intel_sdvo_read_response(intel_output, &response, 2);
 
-       DRM_DEBUG("SDVO response %d %d\n", response & 0xff, response >> 8);
+       DRM_DEBUG_KMS("SDVO response %d %d\n", response & 0xff, response >> 8);
 
        if (status != SDVO_CMD_STATUS_SUCCESS)
                return connector_status_unknown;
@@ -1792,6 +1912,45 @@ static int intel_sdvo_get_modes(struct drm_connector *connector)
        return 1;
 }
 
+static
+void intel_sdvo_destroy_enhance_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 drm_device *dev = connector->dev;
+
+       if (sdvo_priv->is_tv) {
+               if (sdvo_priv->left_property)
+                       drm_property_destroy(dev, sdvo_priv->left_property);
+               if (sdvo_priv->right_property)
+                       drm_property_destroy(dev, sdvo_priv->right_property);
+               if (sdvo_priv->top_property)
+                       drm_property_destroy(dev, sdvo_priv->top_property);
+               if (sdvo_priv->bottom_property)
+                       drm_property_destroy(dev, sdvo_priv->bottom_property);
+               if (sdvo_priv->hpos_property)
+                       drm_property_destroy(dev, sdvo_priv->hpos_property);
+               if (sdvo_priv->vpos_property)
+                       drm_property_destroy(dev, sdvo_priv->vpos_property);
+       }
+       if (sdvo_priv->is_tv) {
+               if (sdvo_priv->saturation_property)
+                       drm_property_destroy(dev,
+                                       sdvo_priv->saturation_property);
+               if (sdvo_priv->contrast_property)
+                       drm_property_destroy(dev,
+                                       sdvo_priv->contrast_property);
+               if (sdvo_priv->hue_property)
+                       drm_property_destroy(dev, sdvo_priv->hue_property);
+       }
+       if (sdvo_priv->is_tv || sdvo_priv->is_lvds) {
+               if (sdvo_priv->brightness_property)
+                       drm_property_destroy(dev,
+                                       sdvo_priv->brightness_property);
+       }
+       return;
+}
+
 static void intel_sdvo_destroy(struct drm_connector *connector)
 {
        struct intel_output *intel_output = to_intel_output(connector);
@@ -1812,6 +1971,9 @@ static void intel_sdvo_destroy(struct drm_connector *connector)
                drm_property_destroy(connector->dev,
                                     sdvo_priv->tv_format_property);
 
+       if (sdvo_priv->is_tv || sdvo_priv->is_lvds)
+               intel_sdvo_destroy_enhance_property(connector);
+
        drm_sysfs_connector_remove(connector);
        drm_connector_cleanup(connector);
 
@@ -1829,6 +1991,8 @@ intel_sdvo_set_property(struct drm_connector *connector,
        struct drm_crtc *crtc = encoder->crtc;
        int ret = 0;
        bool changed = false;
+       uint8_t cmd, status;
+       uint16_t temp_value;
 
        ret = drm_connector_property_set_value(connector, property, val);
        if (ret < 0)
@@ -1845,11 +2009,102 @@ intel_sdvo_set_property(struct drm_connector *connector,
 
                sdvo_priv->tv_format_name = sdvo_priv->tv_format_supported[val];
                changed = true;
-       } else {
-               ret = -EINVAL;
-               goto out;
        }
 
+       if (sdvo_priv->is_tv || sdvo_priv->is_lvds) {
+               cmd = 0;
+               temp_value = val;
+               if (sdvo_priv->left_property == property) {
+                       drm_connector_property_set_value(connector,
+                               sdvo_priv->right_property, val);
+                       if (sdvo_priv->left_margin == temp_value)
+                               goto out;
+
+                       sdvo_priv->left_margin = temp_value;
+                       sdvo_priv->right_margin = temp_value;
+                       temp_value = sdvo_priv->max_hscan -
+                                       sdvo_priv->left_margin;
+                       cmd = SDVO_CMD_SET_OVERSCAN_H;
+               } else if (sdvo_priv->right_property == property) {
+                       drm_connector_property_set_value(connector,
+                               sdvo_priv->left_property, val);
+                       if (sdvo_priv->right_margin == temp_value)
+                               goto out;
+
+                       sdvo_priv->left_margin = temp_value;
+                       sdvo_priv->right_margin = temp_value;
+                       temp_value = sdvo_priv->max_hscan -
+                               sdvo_priv->left_margin;
+                       cmd = SDVO_CMD_SET_OVERSCAN_H;
+               } else if (sdvo_priv->top_property == property) {
+                       drm_connector_property_set_value(connector,
+                               sdvo_priv->bottom_property, val);
+                       if (sdvo_priv->top_margin == temp_value)
+                               goto out;
+
+                       sdvo_priv->top_margin = temp_value;
+                       sdvo_priv->bottom_margin = temp_value;
+                       temp_value = sdvo_priv->max_vscan -
+                                       sdvo_priv->top_margin;
+                       cmd = SDVO_CMD_SET_OVERSCAN_V;
+               } else if (sdvo_priv->bottom_property == property) {
+                       drm_connector_property_set_value(connector,
+                               sdvo_priv->top_property, val);
+                       if (sdvo_priv->bottom_margin == temp_value)
+                               goto out;
+                       sdvo_priv->top_margin = temp_value;
+                       sdvo_priv->bottom_margin = temp_value;
+                       temp_value = sdvo_priv->max_vscan -
+                                       sdvo_priv->top_margin;
+                       cmd = SDVO_CMD_SET_OVERSCAN_V;
+               } else if (sdvo_priv->hpos_property == property) {
+                       if (sdvo_priv->cur_hpos == temp_value)
+                               goto out;
+
+                       cmd = SDVO_CMD_SET_POSITION_H;
+                       sdvo_priv->cur_hpos = temp_value;
+               } else if (sdvo_priv->vpos_property == property) {
+                       if (sdvo_priv->cur_vpos == temp_value)
+                               goto out;
+
+                       cmd = SDVO_CMD_SET_POSITION_V;
+                       sdvo_priv->cur_vpos = temp_value;
+               } else if (sdvo_priv->saturation_property == property) {
+                       if (sdvo_priv->cur_saturation == temp_value)
+                               goto out;
+
+                       cmd = SDVO_CMD_SET_SATURATION;
+                       sdvo_priv->cur_saturation = temp_value;
+               } else if (sdvo_priv->contrast_property == property) {
+                       if (sdvo_priv->cur_contrast == temp_value)
+                               goto out;
+
+                       cmd = SDVO_CMD_SET_CONTRAST;
+                       sdvo_priv->cur_contrast = temp_value;
+               } else if (sdvo_priv->hue_property == property) {
+                       if (sdvo_priv->cur_hue == temp_value)
+                               goto out;
+
+                       cmd = SDVO_CMD_SET_HUE;
+                       sdvo_priv->cur_hue = temp_value;
+               } else if (sdvo_priv->brightness_property == property) {
+                       if (sdvo_priv->cur_brightness == temp_value)
+                               goto out;
+
+                       cmd = SDVO_CMD_SET_BRIGHTNESS;
+                       sdvo_priv->cur_brightness = temp_value;
+               }
+               if (cmd) {
+                       intel_sdvo_write_cmd(intel_output, cmd, &temp_value, 2);
+                       status = intel_sdvo_read_response(intel_output,
+                                                               NULL, 0);
+                       if (status != SDVO_CMD_STATUS_SUCCESS) {
+                               DRM_DEBUG_KMS("Incorrect SDVO command \n");
+                               return -EINVAL;
+                       }
+                       changed = true;
+               }
+       }
        if (changed && crtc)
                drm_crtc_helper_set_mode(crtc, &crtc->mode, crtc->x,
                                crtc->y, crtc->fb);
@@ -2030,6 +2285,25 @@ intel_sdvo_get_slave_addr(struct drm_device *dev, int output_device)
                return 0x72;
 }
 
+static int intel_sdvo_bad_tv_callback(const struct dmi_system_id *id)
+{
+       DRM_DEBUG_KMS("Ignoring bad SDVO TV connector for %s\n", id->ident);
+       return 1;
+}
+
+static struct dmi_system_id intel_sdvo_bad_tv[] = {
+       {
+               .callback = intel_sdvo_bad_tv_callback,
+               .ident = "IntelG45/ICH10R/DME1737",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "IBM CORPORATION"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "4800784"),
+               },
+       },
+
+       { }     /* terminating entry */
+};
+
 static bool
 intel_sdvo_output_setup(struct intel_output *intel_output, uint16_t flags)
 {
@@ -2070,7 +2344,8 @@ intel_sdvo_output_setup(struct intel_output *intel_output, uint16_t flags)
                                        (1 << INTEL_SDVO_NON_TV_CLONE_BIT) |
                                        (1 << INTEL_ANALOG_CLONE_BIT);
                }
-       } else if (flags & SDVO_OUTPUT_SVID0) {
+       } else if ((flags & SDVO_OUTPUT_SVID0) &&
+                  !dmi_check_system(intel_sdvo_bad_tv)) {
 
                sdvo_priv->controlled_output = SDVO_OUTPUT_SVID0;
                encoder->encoder_type = DRM_MODE_ENCODER_TVDAC;
@@ -2090,6 +2365,16 @@ intel_sdvo_output_setup(struct intel_output *intel_output, uint16_t flags)
                sdvo_priv->controlled_output = SDVO_OUTPUT_RGB1;
                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_CVBS0) {
+
+               sdvo_priv->controlled_output = SDVO_OUTPUT_CVBS0;
+               encoder->encoder_type = DRM_MODE_ENCODER_TVDAC;
+               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_LVDS0) {
 
                sdvo_priv->controlled_output = SDVO_OUTPUT_LVDS0;
@@ -2112,10 +2397,9 @@ intel_sdvo_output_setup(struct intel_output *intel_output, uint16_t flags)
 
                sdvo_priv->controlled_output = 0;
                memcpy(bytes, &sdvo_priv->caps.output_flags, 2);
-               DRM_DEBUG_KMS(I915_SDVO,
-                               "%s: Unknown SDVO output type (0x%02x%02x)\n",
-                                 SDVO_NAME(sdvo_priv),
-                                 bytes[0], bytes[1]);
+               DRM_DEBUG_KMS("%s: Unknown SDVO output type (0x%02x%02x)\n",
+                             SDVO_NAME(sdvo_priv),
+                             bytes[0], bytes[1]);
                ret = false;
        }
        intel_output->crtc_mask = (1 << 0) | (1 << 1);
@@ -2177,8 +2461,313 @@ static void intel_sdvo_tv_create_property(struct drm_connector *connector)
 
 }
 
+static void intel_sdvo_create_enhance_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_enhancements_reply sdvo_data;
+       struct drm_device *dev = connector->dev;
+       uint8_t status;
+       uint16_t response, data_value[2];
+
+       intel_sdvo_write_cmd(intel_output, SDVO_CMD_GET_SUPPORTED_ENHANCEMENTS,
+                                               NULL, 0);
+       status = intel_sdvo_read_response(intel_output, &sdvo_data,
+                                       sizeof(sdvo_data));
+       if (status != SDVO_CMD_STATUS_SUCCESS) {
+               DRM_DEBUG_KMS(" incorrect response is returned\n");
+               return;
+       }
+       response = *((uint16_t *)&sdvo_data);
+       if (!response) {
+               DRM_DEBUG_KMS("No enhancement is supported\n");
+               return;
+       }
+       if (sdvo_priv->is_tv) {
+               /* when horizontal overscan is supported, Add the left/right
+                * property
+                */
+               if (sdvo_data.overscan_h) {
+                       intel_sdvo_write_cmd(intel_output,
+                               SDVO_CMD_GET_MAX_OVERSCAN_H, NULL, 0);
+                       status = intel_sdvo_read_response(intel_output,
+                               &data_value, 4);
+                       if (status != SDVO_CMD_STATUS_SUCCESS) {
+                               DRM_DEBUG_KMS("Incorrect SDVO max "
+                                               "h_overscan\n");
+                               return;
+                       }
+                       intel_sdvo_write_cmd(intel_output,
+                               SDVO_CMD_GET_OVERSCAN_H, NULL, 0);
+                       status = intel_sdvo_read_response(intel_output,
+                               &response, 2);
+                       if (status != SDVO_CMD_STATUS_SUCCESS) {
+                               DRM_DEBUG_KMS("Incorrect SDVO h_overscan\n");
+                               return;
+                       }
+                       sdvo_priv->max_hscan = data_value[0];
+                       sdvo_priv->left_margin = data_value[0] - response;
+                       sdvo_priv->right_margin = sdvo_priv->left_margin;
+                       sdvo_priv->left_property =
+                               drm_property_create(dev, DRM_MODE_PROP_RANGE,
+                                               "left_margin", 2);
+                       sdvo_priv->left_property->values[0] = 0;
+                       sdvo_priv->left_property->values[1] = data_value[0];
+                       drm_connector_attach_property(connector,
+                                               sdvo_priv->left_property,
+                                               sdvo_priv->left_margin);
+                       sdvo_priv->right_property =
+                               drm_property_create(dev, DRM_MODE_PROP_RANGE,
+                                               "right_margin", 2);
+                       sdvo_priv->right_property->values[0] = 0;
+                       sdvo_priv->right_property->values[1] = data_value[0];
+                       drm_connector_attach_property(connector,
+                                               sdvo_priv->right_property,
+                                               sdvo_priv->right_margin);
+                       DRM_DEBUG_KMS("h_overscan: max %d, "
+                                       "default %d, current %d\n",
+                                       data_value[0], data_value[1], response);
+               }
+               if (sdvo_data.overscan_v) {
+                       intel_sdvo_write_cmd(intel_output,
+                               SDVO_CMD_GET_MAX_OVERSCAN_V, NULL, 0);
+                       status = intel_sdvo_read_response(intel_output,
+                               &data_value, 4);
+                       if (status != SDVO_CMD_STATUS_SUCCESS) {
+                               DRM_DEBUG_KMS("Incorrect SDVO max "
+                                               "v_overscan\n");
+                               return;
+                       }
+                       intel_sdvo_write_cmd(intel_output,
+                               SDVO_CMD_GET_OVERSCAN_V, NULL, 0);
+                       status = intel_sdvo_read_response(intel_output,
+                               &response, 2);
+                       if (status != SDVO_CMD_STATUS_SUCCESS) {
+                               DRM_DEBUG_KMS("Incorrect SDVO v_overscan\n");
+                               return;
+                       }
+                       sdvo_priv->max_vscan = data_value[0];
+                       sdvo_priv->top_margin = data_value[0] - response;
+                       sdvo_priv->bottom_margin = sdvo_priv->top_margin;
+                       sdvo_priv->top_property =
+                               drm_property_create(dev, DRM_MODE_PROP_RANGE,
+                                               "top_margin", 2);
+                       sdvo_priv->top_property->values[0] = 0;
+                       sdvo_priv->top_property->values[1] = data_value[0];
+                       drm_connector_attach_property(connector,
+                                               sdvo_priv->top_property,
+                                               sdvo_priv->top_margin);
+                       sdvo_priv->bottom_property =
+                               drm_property_create(dev, DRM_MODE_PROP_RANGE,
+                                               "bottom_margin", 2);
+                       sdvo_priv->bottom_property->values[0] = 0;
+                       sdvo_priv->bottom_property->values[1] = data_value[0];
+                       drm_connector_attach_property(connector,
+                                               sdvo_priv->bottom_property,
+                                               sdvo_priv->bottom_margin);
+                       DRM_DEBUG_KMS("v_overscan: max %d, "
+                                       "default %d, current %d\n",
+                                       data_value[0], data_value[1], response);
+               }
+               if (sdvo_data.position_h) {
+                       intel_sdvo_write_cmd(intel_output,
+                               SDVO_CMD_GET_MAX_POSITION_H, NULL, 0);
+                       status = intel_sdvo_read_response(intel_output,
+                               &data_value, 4);
+                       if (status != SDVO_CMD_STATUS_SUCCESS) {
+                               DRM_DEBUG_KMS("Incorrect SDVO Max h_pos\n");
+                               return;
+                       }
+                       intel_sdvo_write_cmd(intel_output,
+                               SDVO_CMD_GET_POSITION_H, NULL, 0);
+                       status = intel_sdvo_read_response(intel_output,
+                               &response, 2);
+                       if (status != SDVO_CMD_STATUS_SUCCESS) {
+                               DRM_DEBUG_KMS("Incorrect SDVO get h_postion\n");
+                               return;
+                       }
+                       sdvo_priv->max_hpos = data_value[0];
+                       sdvo_priv->cur_hpos = response;
+                       sdvo_priv->hpos_property =
+                               drm_property_create(dev, DRM_MODE_PROP_RANGE,
+                                               "hpos", 2);
+                       sdvo_priv->hpos_property->values[0] = 0;
+                       sdvo_priv->hpos_property->values[1] = data_value[0];
+                       drm_connector_attach_property(connector,
+                                               sdvo_priv->hpos_property,
+                                               sdvo_priv->cur_hpos);
+                       DRM_DEBUG_KMS("h_position: max %d, "
+                                       "default %d, current %d\n",
+                                       data_value[0], data_value[1], response);
+               }
+               if (sdvo_data.position_v) {
+                       intel_sdvo_write_cmd(intel_output,
+                               SDVO_CMD_GET_MAX_POSITION_V, NULL, 0);
+                       status = intel_sdvo_read_response(intel_output,
+                               &data_value, 4);
+                       if (status != SDVO_CMD_STATUS_SUCCESS) {
+                               DRM_DEBUG_KMS("Incorrect SDVO Max v_pos\n");
+                               return;
+                       }
+                       intel_sdvo_write_cmd(intel_output,
+                               SDVO_CMD_GET_POSITION_V, NULL, 0);
+                       status = intel_sdvo_read_response(intel_output,
+                               &response, 2);
+                       if (status != SDVO_CMD_STATUS_SUCCESS) {
+                               DRM_DEBUG_KMS("Incorrect SDVO get v_postion\n");
+                               return;
+                       }
+                       sdvo_priv->max_vpos = data_value[0];
+                       sdvo_priv->cur_vpos = response;
+                       sdvo_priv->vpos_property =
+                               drm_property_create(dev, DRM_MODE_PROP_RANGE,
+                                               "vpos", 2);
+                       sdvo_priv->vpos_property->values[0] = 0;
+                       sdvo_priv->vpos_property->values[1] = data_value[0];
+                       drm_connector_attach_property(connector,
+                                               sdvo_priv->vpos_property,
+                                               sdvo_priv->cur_vpos);
+                       DRM_DEBUG_KMS("v_position: max %d, "
+                                       "default %d, current %d\n",
+                                       data_value[0], data_value[1], response);
+               }
+       }
+       if (sdvo_priv->is_tv) {
+               if (sdvo_data.saturation) {
+                       intel_sdvo_write_cmd(intel_output,
+                               SDVO_CMD_GET_MAX_SATURATION, NULL, 0);
+                       status = intel_sdvo_read_response(intel_output,
+                               &data_value, 4);
+                       if (status != SDVO_CMD_STATUS_SUCCESS) {
+                               DRM_DEBUG_KMS("Incorrect SDVO Max sat\n");
+                               return;
+                       }
+                       intel_sdvo_write_cmd(intel_output,
+                               SDVO_CMD_GET_SATURATION, NULL, 0);
+                       status = intel_sdvo_read_response(intel_output,
+                               &response, 2);
+                       if (status != SDVO_CMD_STATUS_SUCCESS) {
+                               DRM_DEBUG_KMS("Incorrect SDVO get sat\n");
+                               return;
+                       }
+                       sdvo_priv->max_saturation = data_value[0];
+                       sdvo_priv->cur_saturation = response;
+                       sdvo_priv->saturation_property =
+                               drm_property_create(dev, DRM_MODE_PROP_RANGE,
+                                               "saturation", 2);
+                       sdvo_priv->saturation_property->values[0] = 0;
+                       sdvo_priv->saturation_property->values[1] =
+                                                       data_value[0];
+                       drm_connector_attach_property(connector,
+                                               sdvo_priv->saturation_property,
+                                               sdvo_priv->cur_saturation);
+                       DRM_DEBUG_KMS("saturation: max %d, "
+                                       "default %d, current %d\n",
+                                       data_value[0], data_value[1], response);
+               }
+               if (sdvo_data.contrast) {
+                       intel_sdvo_write_cmd(intel_output,
+                               SDVO_CMD_GET_MAX_CONTRAST, NULL, 0);
+                       status = intel_sdvo_read_response(intel_output,
+                               &data_value, 4);
+                       if (status != SDVO_CMD_STATUS_SUCCESS) {
+                               DRM_DEBUG_KMS("Incorrect SDVO Max contrast\n");
+                               return;
+                       }
+                       intel_sdvo_write_cmd(intel_output,
+                               SDVO_CMD_GET_CONTRAST, NULL, 0);
+                       status = intel_sdvo_read_response(intel_output,
+                               &response, 2);
+                       if (status != SDVO_CMD_STATUS_SUCCESS) {
+                               DRM_DEBUG_KMS("Incorrect SDVO get contrast\n");
+                               return;
+                       }
+                       sdvo_priv->max_contrast = data_value[0];
+                       sdvo_priv->cur_contrast = response;
+                       sdvo_priv->contrast_property =
+                               drm_property_create(dev, DRM_MODE_PROP_RANGE,
+                                               "contrast", 2);
+                       sdvo_priv->contrast_property->values[0] = 0;
+                       sdvo_priv->contrast_property->values[1] = data_value[0];
+                       drm_connector_attach_property(connector,
+                                               sdvo_priv->contrast_property,
+                                               sdvo_priv->cur_contrast);
+                       DRM_DEBUG_KMS("contrast: max %d, "
+                                       "default %d, current %d\n",
+                                       data_value[0], data_value[1], response);
+               }
+               if (sdvo_data.hue) {
+                       intel_sdvo_write_cmd(intel_output,
+                               SDVO_CMD_GET_MAX_HUE, NULL, 0);
+                       status = intel_sdvo_read_response(intel_output,
+                               &data_value, 4);
+                       if (status != SDVO_CMD_STATUS_SUCCESS) {
+                               DRM_DEBUG_KMS("Incorrect SDVO Max hue\n");
+                               return;
+                       }
+                       intel_sdvo_write_cmd(intel_output,
+                               SDVO_CMD_GET_HUE, NULL, 0);
+                       status = intel_sdvo_read_response(intel_output,
+                               &response, 2);
+                       if (status != SDVO_CMD_STATUS_SUCCESS) {
+                               DRM_DEBUG_KMS("Incorrect SDVO get hue\n");
+                               return;
+                       }
+                       sdvo_priv->max_hue = data_value[0];
+                       sdvo_priv->cur_hue = response;
+                       sdvo_priv->hue_property =
+                               drm_property_create(dev, DRM_MODE_PROP_RANGE,
+                                               "hue", 2);
+                       sdvo_priv->hue_property->values[0] = 0;
+                       sdvo_priv->hue_property->values[1] =
+                                                       data_value[0];
+                       drm_connector_attach_property(connector,
+                                               sdvo_priv->hue_property,
+                                               sdvo_priv->cur_hue);
+                       DRM_DEBUG_KMS("hue: max %d, default %d, current %d\n",
+                                       data_value[0], data_value[1], response);
+               }
+       }
+       if (sdvo_priv->is_tv || sdvo_priv->is_lvds) {
+               if (sdvo_data.brightness) {
+                       intel_sdvo_write_cmd(intel_output,
+                               SDVO_CMD_GET_MAX_BRIGHTNESS, NULL, 0);
+                       status = intel_sdvo_read_response(intel_output,
+                               &data_value, 4);
+                       if (status != SDVO_CMD_STATUS_SUCCESS) {
+                               DRM_DEBUG_KMS("Incorrect SDVO Max bright\n");
+                               return;
+                       }
+                       intel_sdvo_write_cmd(intel_output,
+                               SDVO_CMD_GET_BRIGHTNESS, NULL, 0);
+                       status = intel_sdvo_read_response(intel_output,
+                               &response, 2);
+                       if (status != SDVO_CMD_STATUS_SUCCESS) {
+                               DRM_DEBUG_KMS("Incorrect SDVO get brigh\n");
+                               return;
+                       }
+                       sdvo_priv->max_brightness = data_value[0];
+                       sdvo_priv->cur_brightness = response;
+                       sdvo_priv->brightness_property =
+                               drm_property_create(dev, DRM_MODE_PROP_RANGE,
+                                               "brightness", 2);
+                       sdvo_priv->brightness_property->values[0] = 0;
+                       sdvo_priv->brightness_property->values[1] =
+                                                       data_value[0];
+                       drm_connector_attach_property(connector,
+                                               sdvo_priv->brightness_property,
+                                               sdvo_priv->cur_brightness);
+                       DRM_DEBUG_KMS("brightness: max %d, "
+                                       "default %d, current %d\n",
+                                       data_value[0], data_value[1], response);
+               }
+       }
+       return;
+}
+
 bool intel_sdvo_init(struct drm_device *dev, int output_device)
 {
+       struct drm_i915_private *dev_priv = dev->dev_private;
        struct drm_connector *connector;
        struct intel_output *intel_output;
        struct intel_sdvo_priv *sdvo_priv;
@@ -2214,8 +2803,7 @@ bool intel_sdvo_init(struct drm_device *dev, int output_device)
        /* Read the regs to test if we can talk to the device */
        for (i = 0; i < 0x40; i++) {
                if (!intel_sdvo_read_byte(intel_output, i, &ch[i])) {
-                       DRM_DEBUG_KMS(I915_SDVO,
-                                       "No SDVO device found on SDVO%c\n",
+                       DRM_DEBUG_KMS("No SDVO device found on SDVO%c\n",
                                        output_device == SDVOB ? 'B' : 'C');
                        goto err_i2c;
                }
@@ -2226,10 +2814,12 @@ bool intel_sdvo_init(struct drm_device *dev, int output_device)
                intel_output->ddc_bus = intel_i2c_create(dev, GPIOE, "SDVOB DDC BUS");
                sdvo_priv->analog_ddc_bus = intel_i2c_create(dev, GPIOA,
                                                "SDVOB/VGA DDC BUS");
+               dev_priv->hotplug_supported_mask |= SDVOB_HOTPLUG_INT_STATUS;
        } 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");
+               dev_priv->hotplug_supported_mask |= SDVOC_HOTPLUG_INT_STATUS;
        }
 
        if (intel_output->ddc_bus == NULL)
@@ -2238,12 +2828,12 @@ bool intel_sdvo_init(struct drm_device *dev, int output_device)
        /* Wrap with our custom algo which switches to DDC mode */
        intel_output->ddc_bus->algo = &intel_sdvo_i2c_bit_algo;
 
-       /* In defaut case sdvo lvds is false */
+       /* In default case sdvo lvds is false */
        intel_sdvo_get_capabilities(intel_output, &sdvo_priv->caps);
 
        if (intel_sdvo_output_setup(intel_output,
                                    sdvo_priv->caps.output_flags) != true) {
-               DRM_DEBUG("SDVO output failed to setup on SDVO%c\n",
+               DRM_DEBUG_KMS("SDVO output failed to setup on SDVO%c\n",
                          output_device == SDVOB ? 'B' : 'C');
                goto err_i2c;
        }
@@ -2266,6 +2856,10 @@ bool intel_sdvo_init(struct drm_device *dev, int output_device)
        drm_mode_connector_attach_encoder(&intel_output->base, &intel_output->enc);
        if (sdvo_priv->is_tv)
                intel_sdvo_tv_create_property(connector);
+
+       if (sdvo_priv->is_tv || sdvo_priv->is_lvds)
+               intel_sdvo_create_enhance_property(connector);
+
        drm_sysfs_connector_add(connector);
 
        intel_sdvo_select_ddc_bus(sdvo_priv);
@@ -2278,7 +2872,7 @@ bool intel_sdvo_init(struct drm_device *dev, int output_device)
                                               &sdvo_priv->pixel_clock_max);
 
 
-       DRM_DEBUG_KMS(I915_SDVO, "%s device VID/DID: %02X:%02X.%02X, "
+       DRM_DEBUG_KMS("%s device VID/DID: %02X:%02X.%02X, "
                        "clock range %dMHz - %dMHz, "
                        "input 1: %c, input 2: %c, "
                        "output 1: %c, output 2: %c\n",