aboutsummaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/i915/intel_sdvo.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/i915/intel_sdvo.c')
-rw-r--r--drivers/gpu/drm/i915/intel_sdvo.c307
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)