From b9750a328c81fa0a2e81c0be197aea3cc8141f36 Mon Sep 17 00:00:00 2001 From: Zhenyu Wang Date: Wed, 17 Dec 2008 10:17:27 +0800 Subject: [PATCH] SDVO: handle multifunction encoder For SDVO encoder that advertise multiple functions, we have to get current attached display to determine output type. --- src/i830_sdvo.c | 276 +++++++++++++++++++++++++++++++++++-------------------- 1 files changed, 178 insertions(+), 98 deletions(-) diff --git a/src/i830_sdvo.c b/src/i830_sdvo.c index 256d16d..838c3a0 100644 --- a/src/i830_sdvo.c +++ b/src/i830_sdvo.c @@ -66,6 +66,11 @@ struct i830_sdvo_priv { */ struct i830_sdvo_caps caps; + /** + * SDVO devices currently attached by i830_sdvo_get_attached_display() + */ + uint16_t attached_output; + /** Pixel clock limitations reported by the SDVO device, in kHz */ int pixel_clock_min, pixel_clock_max; @@ -1450,33 +1455,19 @@ i830_sdvo_dump(ScrnInfoPtr pScrn) } } -/** - * Asks the SDVO device if any displays are currently connected. - * - * This interface will need to be augmented, since we could potentially have - * multiple displays connected, and the caller will also probably want to know - * what type of display is connected. But this is enough for the moment. - * - * Takes 14ms on average on my i945G. - */ -static xf86OutputStatus -i830_sdvo_detect(xf86OutputPtr output) +static Bool +i830_sdvo_get_attached_display (xf86OutputPtr output, uint16_t* response) { - uint8_t response[2]; uint8_t status; i830_sdvo_write_cmd(output, SDVO_CMD_GET_ATTACHED_DISPLAYS, NULL, 0); - status = i830_sdvo_read_response(output, &response, 2); - + status = i830_sdvo_read_response(output, response, 2); if (status != SDVO_CMD_STATUS_SUCCESS) - return XF86OutputStatusUnknown; - - if (response[0] != 0 || response[1] != 0) - return XF86OutputStatusConnected; - else - return XF86OutputStatusDisconnected; + return FALSE; + return TRUE; } + static DisplayModePtr i830_sdvo_get_ddc_modes(xf86OutputPtr output) { @@ -1663,22 +1654,6 @@ i830_sdvo_get_crtc(xf86OutputPtr output) } #endif -static const xf86OutputFuncsRec i830_sdvo_output_funcs = { - .dpms = i830_sdvo_dpms, - .save = i830_sdvo_save, - .restore = i830_sdvo_restore, - .mode_valid = i830_sdvo_mode_valid, - .mode_fixup = i830_sdvo_mode_fixup, - .prepare = i830_output_prepare, - .mode_set = i830_sdvo_mode_set, - .commit = i830_output_commit, - .detect = i830_sdvo_detect, - .get_modes = i830_sdvo_get_modes, - .destroy = i830_sdvo_destroy, -#ifdef RANDR_GET_CRTC_INTERFACE - .get_crtc = i830_sdvo_get_crtc, -#endif -}; static unsigned int count_bits(uint32_t mask) { @@ -1750,6 +1725,158 @@ i830_sdvo_get_digital_encoding_mode(xf86OutputPtr output) return TRUE; } +static Bool +i830_sdvo_output_setup(xf86OutputPtr output, uint16_t flag) +{ + I830OutputPrivatePtr intel_output = output->driver_private; + struct i830_sdvo_priv *dev_priv = intel_output->dev_priv; + char name[60]; + char *name_prefix; + char *name_suffix; + + if (dev_priv->output_device == SDVOB) + name_suffix="-1"; + else + name_suffix="-2"; + + if (flag & (SDVO_OUTPUT_TMDS0 | SDVO_OUTPUT_TMDS1)) + { + if (flag & SDVO_OUTPUT_TMDS0) + dev_priv->controlled_output = SDVO_OUTPUT_TMDS0; + else + dev_priv->controlled_output = SDVO_OUTPUT_TMDS1; + output->subpixel_order = SubPixelHorizontalRGB; + name_prefix="TMDS"; + + if (i830_sdvo_get_supp_encode(output, &dev_priv->encode) && + i830_sdvo_get_digital_encoding_mode(output) && + dev_priv->is_hdmi) { + /* enable hdmi encoding mode if supported */ + i830_sdvo_set_encode(output, SDVO_ENCODE_HDMI); + i830_sdvo_set_colorimetry(output, SDVO_COLORIMETRY_RGB256); + name_prefix = "HDMI"; + } + } + else if (flag & SDVO_OUTPUT_RGB0) + { + dev_priv->controlled_output = SDVO_OUTPUT_RGB0; + output->subpixel_order = SubPixelHorizontalRGB; + name_prefix="VGA"; + } + else if (flag & SDVO_OUTPUT_RGB1) + { + dev_priv->controlled_output = SDVO_OUTPUT_RGB1; + output->subpixel_order = SubPixelHorizontalRGB; + name_prefix="VGA"; + } + else if (flag & SDVO_OUTPUT_SVID0) + { + dev_priv->controlled_output = SDVO_OUTPUT_SVID0; + output->subpixel_order = SubPixelHorizontalRGB; /* XXX */ + name_prefix="TV"; + dev_priv->is_tv = TRUE; + intel_output->needs_tv_clock = TRUE; + } + else + { + unsigned char bytes[2]; + + dev_priv->controlled_output = 0; + memcpy (bytes, &flag, 2); + xf86DrvMsg(intel_output->pI2CBus->scrnIndex, X_WARNING, + "%s: Unknown SDVO output type (0x%02x%02x)\n", + SDVO_NAME(dev_priv), + bytes[0], bytes[1]); + name_prefix="Unknown"; + } + + strcpy (name, name_prefix); + strcat (name, name_suffix); + if (!xf86OutputRename (output, name)) { + ErrorF("zhen: output rename failed, %s -> %s\n", + output->name, name); + return FALSE; + } + + i830_sdvo_select_ddc_bus(dev_priv); + + return TRUE; +} + +static Bool +i830_sdvo_multifunc_encoder(xf86OutputPtr output) +{ + I830OutputPrivatePtr intel_output = output->driver_private; + struct i830_sdvo_priv *dev_priv = intel_output->dev_priv; + int i = 0; + + if (dev_priv->caps.output_flags & (SDVO_OUTPUT_TMDS0 | + SDVO_OUTPUT_TMDS1)) + i++; + if (dev_priv->caps.output_flags & (SDVO_OUTPUT_RGB0 | + SDVO_OUTPUT_RGB1)) + i++; + if (dev_priv->caps.output_flags & (SDVO_OUTPUT_CVBS0 | + SDVO_OUTPUT_SVID0 | SDVO_OUTPUT_YPRPB0 | + SDVO_OUTPUT_SCART0 | SDVO_OUTPUT_CVBS1 | + SDVO_OUTPUT_SVID1 | SDVO_OUTPUT_YPRPB1 | + SDVO_OUTPUT_SCART1)) + i++; + if (dev_priv->caps.output_flags & (SDVO_OUTPUT_LVDS0 | + SDVO_OUTPUT_LVDS1)) + i++; + return (i > 1); +} + +/** + * Asks the SDVO device if any displays are currently connected. + * + * This interface will need to be augmented, since we could potentially have + * multiple displays connected, and the caller will also probably want to know + * what type of display is connected. But this is enough for the moment. + * + * Takes 14ms on average on my i945G. + */ +static xf86OutputStatus +i830_sdvo_detect(xf86OutputPtr output) +{ + I830OutputPrivatePtr intel_output = output->driver_private; + struct i830_sdvo_priv *dev_priv = intel_output->dev_priv; + + if (i830_sdvo_get_attached_display(output, &dev_priv->attached_output) + == FALSE) + return XF86OutputStatusUnknown; + + if (dev_priv->attached_output == 0) + return XF86OutputStatusDisconnected; + + if (i830_sdvo_multifunc_encoder(output) && + i830_sdvo_output_setup(output, dev_priv->attached_output) + == FALSE) { + ErrorF("zhen: output setup failed\n"); + return XF86OutputStatusUnknown; + } + + return XF86OutputStatusConnected; +} + +static const xf86OutputFuncsRec i830_sdvo_output_funcs = { + .dpms = i830_sdvo_dpms, + .save = i830_sdvo_save, + .restore = i830_sdvo_restore, + .mode_valid = i830_sdvo_mode_valid, + .mode_fixup = i830_sdvo_mode_fixup, + .prepare = i830_output_prepare, + .mode_set = i830_sdvo_mode_set, + .commit = i830_output_commit, + .detect = i830_sdvo_detect, + .get_modes = i830_sdvo_get_modes, + .destroy = i830_sdvo_destroy, +#ifdef RANDR_GET_CRTC_INTERFACE + .get_crtc = i830_sdvo_get_crtc, +#endif +}; + Bool i830_sdvo_init(ScrnInfoPtr pScrn, int output_device) { @@ -1759,11 +1886,9 @@ i830_sdvo_init(ScrnInfoPtr pScrn, int output_device) int i; unsigned char ch[0x40]; I2CBusPtr i2cbus = NULL, ddcbus; - char name[60]; - char *name_prefix; - char *name_suffix; - output = xf86OutputCreate (pScrn, &i830_sdvo_output_funcs,NULL); + output = xf86OutputCreate (pScrn, &i830_sdvo_output_funcs, + (output_device == SDVOB) ? "SDVOB" : "SDVOC"); if (!output) return FALSE; intel_output = xnfcalloc (sizeof (I830OutputPrivateRec) + @@ -1801,11 +1926,9 @@ i830_sdvo_init(ScrnInfoPtr pScrn, int output_device) if (output_device == SDVOB) { dev_priv->d.DevName = "SDVO Controller B"; dev_priv->d.SlaveAddr = 0x70; - name_suffix="-1"; } else { dev_priv->d.DevName = "SDVO Controller C"; dev_priv->d.SlaveAddr = 0x72; - name_suffix="-2"; } dev_priv->d.pI2CBus = i2cbus; dev_priv->d.DriverPrivate.ptr = output; @@ -1866,69 +1989,25 @@ i830_sdvo_init(ScrnInfoPtr pScrn, int output_device) intel_output->pDDCBus = ddcbus; intel_output->dev_priv = dev_priv; - i830_sdvo_get_capabilities(output, &dev_priv->caps); - - if (dev_priv->caps.output_flags & (SDVO_OUTPUT_TMDS0 | SDVO_OUTPUT_TMDS1)) + if (i830_sdvo_get_capabilities(output, &dev_priv->caps) == FALSE || + dev_priv->caps.output_flags == 0) { - if (dev_priv->caps.output_flags & SDVO_OUTPUT_TMDS0) - dev_priv->controlled_output = SDVO_OUTPUT_TMDS0; - else - dev_priv->controlled_output = SDVO_OUTPUT_TMDS1; - output->subpixel_order = SubPixelHorizontalRGB; - name_prefix="TMDS"; - - if (i830_sdvo_get_supp_encode(output, &dev_priv->encode) && - i830_sdvo_get_digital_encoding_mode(output) && - dev_priv->is_hdmi) { - /* enable hdmi encoding mode if supported */ - i830_sdvo_set_encode(output, SDVO_ENCODE_HDMI); - i830_sdvo_set_colorimetry(output, SDVO_COLORIMETRY_RGB256); - name_prefix = "HDMI"; - } - } - else if (dev_priv->caps.output_flags & SDVO_OUTPUT_SVID0) - { - dev_priv->controlled_output = SDVO_OUTPUT_SVID0; - output->subpixel_order = SubPixelHorizontalRGB; /* XXX */ - name_prefix="TV"; - dev_priv->is_tv = TRUE; - intel_output->needs_tv_clock = TRUE; - } - else if (dev_priv->caps.output_flags & SDVO_OUTPUT_RGB0) - { - dev_priv->controlled_output = SDVO_OUTPUT_RGB0; - output->subpixel_order = SubPixelHorizontalRGB; - name_prefix="VGA"; - } - else if (dev_priv->caps.output_flags & SDVO_OUTPUT_RGB1) - { - dev_priv->controlled_output = SDVO_OUTPUT_RGB1; - output->subpixel_order = SubPixelHorizontalRGB; - name_prefix="VGA"; - } - else - { - unsigned char bytes[2]; - - dev_priv->controlled_output = 0; - memcpy (bytes, &dev_priv->caps.output_flags, 2); xf86DrvMsg(intel_output->pI2CBus->scrnIndex, X_WARNING, - "%s: Unknown SDVO output type (0x%02x%02x)\n", - SDVO_NAME(dev_priv), - bytes[0], bytes[1]); - name_prefix="Unknown"; + "SDVO%c can't get encoder capability\n", + output_device == SDVOB ? 'B' : 'C'); + xf86OutputDestroy (output); + return FALSE; } - strcpy (name, name_prefix); - strcat (name, name_suffix); - if (!xf86OutputRename (output, name)) - { + /* if capability flags has no multiple function outputs, + setup output, else leave to ->detect time */ + if (!i830_sdvo_multifunc_encoder(output) && + i830_sdvo_output_setup(output, dev_priv->caps.output_flags) + == FALSE) { xf86OutputDestroy (output); return FALSE; } - i830_sdvo_select_ddc_bus(dev_priv); - /* Set the input timing to the screen. Assume always input 0. */ i830_sdvo_set_target_input(output, TRUE, FALSE); @@ -1936,11 +2015,12 @@ i830_sdvo_init(ScrnInfoPtr pScrn, int output_device) &dev_priv->pixel_clock_max); xf86DrvMsg(pScrn->scrnIndex, X_INFO, - "%s: device VID/DID: %02X:%02X.%02X, " + "%s: device VID/DID: %02X:%02X.%02X cap: 0x%x " "clock range %.1fMHz - %.1fMHz\n", SDVO_NAME(dev_priv), dev_priv->caps.vendor_id, dev_priv->caps.device_id, dev_priv->caps.device_rev_id, + dev_priv->caps.output_flags, dev_priv->pixel_clock_min / 1000.0, dev_priv->pixel_clock_max / 1000.0); -- 1.5.6.5