diff options
author | Eric Anholt <eric@anholt.net> | 2006-10-31 14:32:00 -0800 |
---|---|---|
committer | Eric Anholt <eric@anholt.net> | 2006-10-31 14:32:00 -0800 |
commit | 68cef9f4e028755bbf3e1862da2ef47d46ddaa6a (patch) | |
tree | 1f5018a6e0492efff7130b97b18864d00df1d4ad | |
parent | 9fd719fce27f916ab5120f6e1234affa14eaed9d (diff) |
Move output connection detection to a per-output method.
This will be used by RandR, and should let us clean up some of the initial
display configuration, hopefully.
Also, analog hotplug-based detection is now enabled on G965.
-rw-r--r-- | src/i830.h | 11 | ||||
-rw-r--r-- | src/i830_crt.c | 162 | ||||
-rw-r--r-- | src/i830_display.c | 147 | ||||
-rw-r--r-- | src/i830_display.h | 1 | ||||
-rw-r--r-- | src/i830_driver.c | 59 | ||||
-rw-r--r-- | src/i830_dvo.c | 12 | ||||
-rw-r--r-- | src/i830_lvds.c | 14 | ||||
-rw-r--r-- | src/i830_modes.c | 13 | ||||
-rw-r--r-- | src/i830_sdvo.c | 12 | ||||
-rw-r--r-- | src/i830_sdvo.h | 3 | ||||
-rw-r--r-- | src/i830_tv.c | 13 |
11 files changed, 261 insertions, 186 deletions
@@ -207,6 +207,12 @@ struct _I830DVODriver { extern const char *i830_output_type_names[]; +enum detect_status { + OUTPUT_STATUS_CONNECTED, + OUTPUT_STATUS_DISCONNECTED, + OUTPUT_STATUS_UNKNOWN +}; + struct _I830OutputRec { int type; int pipe; @@ -258,6 +264,11 @@ struct _I830OutputRec { void (*post_set_mode)(ScrnInfoPtr pScrn, I830OutputPtr output, DisplayModePtr pMode); + /** + * Probe for a connected output, and return detect_status. + */ + enum detect_status (*detect)(ScrnInfoPtr pScrn, I830OutputPtr output); + xf86MonPtr MonInfo; I2CBusPtr pI2CBus; I2CBusPtr pDDCBus; diff --git a/src/i830_crt.c b/src/i830_crt.c index 7721a0c2..d271eedb 100644 --- a/src/i830_crt.c +++ b/src/i830_crt.c @@ -112,6 +112,167 @@ i830_crt_post_set_mode(ScrnInfoPtr pScrn, I830OutputPtr output, OUTREG(ADPA, adpa); } +/** + * Uses CRT_HOTPLUG_EN and CRT_HOTPLUG_STAT to detect CRT presence. + * + * Only for I945G/GM. + * + * \return TRUE if CRT is connected. + * \return FALSE if CRT is disconnected. + */ +static Bool +i830_crt_detect_hotplug(ScrnInfoPtr pScrn) +{ + I830Ptr pI830 = I830PTR(pScrn); + CARD32 temp; + const int timeout_ms = 1000; + int starttime, curtime; + + temp = INREG(PORT_HOTPLUG_EN); + + OUTREG(PORT_HOTPLUG_EN, temp | CRT_HOTPLUG_FORCE_DETECT | (1 << 5)); + + for (curtime = starttime = GetTimeInMillis(); + (curtime - starttime) < timeout_ms; curtime = GetTimeInMillis()) + { + if ((INREG(PORT_HOTPLUG_EN) & CRT_HOTPLUG_FORCE_DETECT) == 0) + break; + } + + if ((INREG(PORT_HOTPLUG_STAT) & CRT_HOTPLUG_MONITOR_MASK) == + CRT_HOTPLUG_MONITOR_COLOR) + { + return TRUE; + } else { + return FALSE; + } +} + +/** + * Detects CRT presence by checking for load. + * + * Requires that the current pipe's DPLL is active. This will cause flicker + * on the CRT, so it should not be used while the display is being used. Only + * color (not monochrome) displays are detected. + * + * \return TRUE if CRT is connected. + * \return FALSE if CRT is disconnected. + */ +static Bool +i830_crt_detect_load(ScrnInfoPtr pScrn) +{ + I830Ptr pI830 = I830PTR(pScrn); + CARD32 adpa, pipeconf; + CARD8 st00; + int pipeconf_reg, bclrpat_reg, dpll_reg; + int pipe; + + pipe = pI830->pipe; + if (pipe == 0) { + bclrpat_reg = BCLRPAT_A; + pipeconf_reg = PIPEACONF; + dpll_reg = DPLL_A; + } else { + bclrpat_reg = BCLRPAT_B; + pipeconf_reg = PIPEBCONF; + dpll_reg = DPLL_B; + } + + /* Don't try this if the DPLL isn't running. */ + if (!(INREG(dpll_reg) & DPLL_VCO_ENABLE)) + return FALSE; + + adpa = INREG(ADPA); + + /* Enable CRT output if disabled. */ + if (!(adpa & ADPA_DAC_ENABLE)) { + OUTREG(ADPA, adpa | ADPA_DAC_ENABLE | + ((pipe == 1) ? ADPA_PIPE_B_SELECT : 0)); + } + + /* Set the border color to red, green. Maybe we should save/restore this + * reg. + */ + OUTREG(bclrpat_reg, 0x00500050); + + /* Force the border color through the active region */ + pipeconf = INREG(pipeconf_reg); + OUTREG(pipeconf_reg, pipeconf | PIPECONF_FORCE_BORDER); + + /* Read the ST00 VGA status register */ + st00 = pI830->readStandard(pI830, 0x3c2); + + /* Restore previous settings */ + OUTREG(pipeconf_reg, pipeconf); + OUTREG(ADPA, adpa); + + if (st00 & (1 << 4)) + return TRUE; + else + return FALSE; +} + +/** + * Detects CRT presence by probing for a response on the DDC address. + * + * This takes approximately 5ms in testing on an i915GM, with CRT connected or + * not. + * + * \return TRUE if the CRT is connected and responded to DDC. + * \return FALSE if no DDC response was detected. + */ +static Bool +i830_crt_detect_ddc(ScrnInfoPtr pScrn) +{ + I830Ptr pI830 = I830PTR(pScrn); + struct _I830OutputRec *output; + + output = &pI830->output[0]; + /* CRT should always be at 0, but check anyway */ + if (output->type != I830_OUTPUT_ANALOG) + return FALSE; + + return xf86I2CProbeAddress(output->pDDCBus, 0x00A0); +} + +/** + * Attempts to detect CRT presence through any method available. + * + * @param allow_disturb enables detection methods that may cause flickering + * on active displays. + */ +static enum detect_status +i830_crt_detect(ScrnInfoPtr pScrn, I830OutputPtr output) +{ + I830Ptr pI830 = I830PTR(pScrn); + + if (IS_I945G(pI830) || IS_I945GM(pI830) || IS_I965G(pI830)) { + if (i830_crt_detect_hotplug(pScrn)) + return OUTPUT_STATUS_CONNECTED; + else + return OUTPUT_STATUS_DISCONNECTED; + } + + if (i830_crt_detect_ddc(pScrn)) + return OUTPUT_STATUS_CONNECTED; + + /* Use the load-detect method if we're not currently outputting to the CRT, + * or we don't care. + * + * Actually, the method is unreliable currently. We need to not share a + * pipe, as it seems having other outputs on that pipe will result in a + * false positive. + */ + if (0) { + if (i830_crt_detect_load(pScrn)) + return OUTPUT_STATUS_CONNECTED; + else + return OUTPUT_STATUS_DISCONNECTED; + } + + return OUTPUT_STATUS_UNKNOWN; +} + void i830_crt_init(ScrnInfoPtr pScrn) { @@ -124,6 +285,7 @@ i830_crt_init(ScrnInfoPtr pScrn) pI830->output[pI830->num_outputs].mode_valid = i830_crt_mode_valid; pI830->output[pI830->num_outputs].pre_set_mode = i830_crt_pre_set_mode; pI830->output[pI830->num_outputs].post_set_mode = i830_crt_post_set_mode; + pI830->output[pI830->num_outputs].detect = i830_crt_detect; /* Set up the DDC bus. */ I830I2CInit(pScrn, &pI830->output[pI830->num_outputs].pDDCBus, diff --git a/src/i830_display.c b/src/i830_display.c index 41f8c213..3151fd10 100644 --- a/src/i830_display.c +++ b/src/i830_display.c @@ -831,150 +831,3 @@ i830DescribeOutputConfiguration(ScrnInfoPtr pScrn) pI830->output[i].pipe == 0 ? 'A' : 'B'); } } - -/** - * Uses CRT_HOTPLUG_EN and CRT_HOTPLUG_STAT to detect CRT presence. - * - * Only for I945G/GM. - */ -static Bool -i830HotplugDetectCRT(ScrnInfoPtr pScrn) -{ - I830Ptr pI830 = I830PTR(pScrn); - CARD32 temp; - const int timeout_ms = 1000; - int starttime, curtime; - - temp = INREG(PORT_HOTPLUG_EN); - - OUTREG(PORT_HOTPLUG_EN, temp | CRT_HOTPLUG_FORCE_DETECT | (1 << 5)); - - for (curtime = starttime = GetTimeInMillis(); - (curtime - starttime) < timeout_ms; curtime = GetTimeInMillis()) - { - if ((INREG(PORT_HOTPLUG_EN) & CRT_HOTPLUG_FORCE_DETECT) == 0) - break; - } - - if ((INREG(PORT_HOTPLUG_STAT) & CRT_HOTPLUG_MONITOR_MASK) == - CRT_HOTPLUG_MONITOR_COLOR) - { - return TRUE; - } else { - return FALSE; - } -} - -/** - * Detects CRT presence by checking for load. - * - * Requires that the current pipe's DPLL is active. This will cause flicker - * on the CRT, so it should not be used while the display is being used. Only - * color (not monochrome) displays are detected. - */ -static Bool -i830LoadDetectCRT(ScrnInfoPtr pScrn) -{ - I830Ptr pI830 = I830PTR(pScrn); - CARD32 adpa, pipeconf; - CARD8 st00; - int pipeconf_reg, bclrpat_reg, dpll_reg; - int pipe; - - pipe = pI830->pipe; - if (pipe == 0) { - bclrpat_reg = BCLRPAT_A; - pipeconf_reg = PIPEACONF; - dpll_reg = DPLL_A; - } else { - bclrpat_reg = BCLRPAT_B; - pipeconf_reg = PIPEBCONF; - dpll_reg = DPLL_B; - } - - /* Don't try this if the DPLL isn't running. */ - if (!(INREG(dpll_reg) & DPLL_VCO_ENABLE)) - return FALSE; - - adpa = INREG(ADPA); - - /* Enable CRT output if disabled. */ - if (!(adpa & ADPA_DAC_ENABLE)) { - OUTREG(ADPA, adpa | ADPA_DAC_ENABLE | - ((pipe == 1) ? ADPA_PIPE_B_SELECT : 0)); - } - - /* Set the border color to red, green. Maybe we should save/restore this - * reg. - */ - OUTREG(bclrpat_reg, 0x00500050); - - /* Force the border color through the active region */ - pipeconf = INREG(pipeconf_reg); - OUTREG(pipeconf_reg, pipeconf | PIPECONF_FORCE_BORDER); - - /* Read the ST00 VGA status register */ - st00 = pI830->readStandard(pI830, 0x3c2); - - /* Restore previous settings */ - OUTREG(pipeconf_reg, pipeconf); - OUTREG(ADPA, adpa); - - if (st00 & (1 << 4)) - return TRUE; - else - return FALSE; -} - -/** - * Detects CRT presence by probing for a response on the DDC address. - * - * This takes approximately 5ms in testing on an i915GM, with CRT connected or - * not. - */ -static Bool -i830DDCDetectCRT(ScrnInfoPtr pScrn) -{ - I830Ptr pI830 = I830PTR(pScrn); - struct _I830OutputRec *output; - - output = &pI830->output[0]; - /* CRT should always be at 0, but check anyway */ - if (output->type != I830_OUTPUT_ANALOG) - return FALSE; - - return xf86I2CProbeAddress(output->pDDCBus, 0x00A0); -} - -/** - * Attempts to detect CRT presence through any method available. - * - * @param allow_disturb enables detection methods that may cause flickering - * on active displays. - */ -Bool -i830DetectCRT(ScrnInfoPtr pScrn, Bool allow_disturb) -{ - I830Ptr pI830 = I830PTR(pScrn); - Bool found_ddc; - - if (IS_I945G(pI830) || IS_I945GM(pI830)) - return i830HotplugDetectCRT(pScrn); - - found_ddc = i830DDCDetectCRT(pScrn); - if (found_ddc) - return TRUE; - - /* Use the load-detect method if we're not currently outputting to the CRT, - * or we don't care. - * - * Actually, the method is unreliable currently. We need to not share a - * pipe, as it seems having other outputs on that pipe will result in a - * false positive. - */ - if (0 && (allow_disturb || !(INREG(ADPA) & !ADPA_DAC_ENABLE))) { - return i830LoadDetectCRT(pScrn); - } - - return FALSE; -} diff --git a/src/i830_display.h b/src/i830_display.h index 576a1491..8a6e9e90 100644 --- a/src/i830_display.h +++ b/src/i830_display.h @@ -29,7 +29,6 @@ Bool i830PipeSetMode(ScrnInfoPtr pScrn, DisplayModePtr pMode, int pipe); void i830DisableUnusedFunctions(ScrnInfoPtr pScrn); Bool i830SetMode(ScrnInfoPtr pScrn, DisplayModePtr pMode); -Bool i830DetectCRT(ScrnInfoPtr pScrn, Bool allow_disturb); void i830PipeSetBase(ScrnInfoPtr pScrn, int pipe, int x, int y); void i830WaitForVblank(ScrnInfoPtr pScrn); void i830DescribeOutputConfiguration(ScrnInfoPtr pScrn); diff --git a/src/i830_driver.c b/src/i830_driver.c index f142c4b2..2c7eca73 100644 --- a/src/i830_driver.c +++ b/src/i830_driver.c @@ -1439,28 +1439,36 @@ I830PreInit(ScrnInfoPtr pScrn, int flags) pI830->operatingDevices = (pI830->MonType2 << 8) | pI830->MonType1; pI830->specifiedMonitor = TRUE; } else if (I830IsPrimary(pScrn)) { - /* Choose a default set of outputs to use based on what we've detected. */ - if (i830DetectCRT(pScrn, TRUE)) { - pI830->MonType1 |= PIPE_CRT; - } - - /* Check for attached SDVO outputs. Assume that they're flat panels for - * now. Though really, it's just a name at the moment, since we don't - * treat different SDVO outputs differently. Also, check for LVDS and - * set it to the right pipe if available. + /* Choose a default set of outputs to use based on what we've detected. + * + * Assume that SDVO outputs are flat panels for now. It's just a name + * at the moment, since we don't treat different SDVO outputs + * differently. */ for (i = 0; i < pI830->num_outputs; i++) { if (pI830->output[i].type == I830_OUTPUT_LVDS) - pI830->MonType2 |= PIPE_LFP; + pI830->MonType2 = PIPE_LFP; + + if (pI830->output[i].type == I830_OUTPUT_SDVO || + pI830->output[i].type == I830_OUTPUT_ANALOG) + { + int pipetype; - if (pI830->output[i].type == I830_OUTPUT_SDVO) { - if (!i830_sdvo_detect_displays(pScrn, &pI830->output[i])) + if (pI830->output[i].detect(pScrn, &pI830->output[i]) == + OUTPUT_STATUS_DISCONNECTED) + { continue; + } + + if (pI830->output[i].type == I830_OUTPUT_SDVO) + pipetype = PIPE_DFP; + else + pipetype = PIPE_CRT; if (pI830->MonType1 == PIPE_NONE) - pI830->MonType1 |= PIPE_DFP; + pI830->MonType1 |= pipetype; else if (pI830->MonType2 == PIPE_NONE) - pI830->MonType2 |= PIPE_DFP; + pI830->MonType2 |= pipetype; } } @@ -3891,22 +3899,23 @@ i830MonitorDetectDebugger(ScrnInfoPtr pScrn) if (!pScrn->vtSema) return 1000; - start = GetTimeInMillis(); - found_crt = i830DetectCRT(pScrn, FALSE); - finish = GetTimeInMillis(); - xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Detected CRT as %s in %dms\n", - found_crt ? "connected" : "disconnected", finish - start); - for (i = 0; i < pI830->num_outputs; i++) { - Bool found_sdvo = TRUE; + enum output_status ret; + char *result; - if (pI830->output[i].type != I830_OUTPUT_SDVO) - continue; start = GetTimeInMillis(); - found_sdvo = i830_sdvo_detect_displays(pScrn, &pI830->output[i]); + ret = pI830->output[i].detect(pScrn, &pI830->output[i]); finish = GetTimeInMillis(); + + if (ret == OUTPUT_STATUS_CONNECTED) + result = "connected"; + else if (ret == OUTPUT_STATUS_DISCONNECTED) + result = "disconnected"; + else + result = "unknown"; + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Detected SDVO as %s in %dms\n", - found_sdvo ? "connected" : "disconnected", finish - start); + result, finish - start); } } #endif diff --git a/src/i830_dvo.c b/src/i830_dvo.c index 01858f2c..ed21ecc5 100644 --- a/src/i830_dvo.c +++ b/src/i830_dvo.c @@ -144,6 +144,17 @@ i830_dvo_post_set_mode(ScrnInfoPtr pScrn, I830OutputPtr output, OUTREG(DVOC, dvo); } +/** + * Detect the output connection on our DVO device. + * + * Unimplemented. + */ +static enum detect_status +i830_dvo_detect(ScrnInfoPtr pScrn, I830OutputPtr output) +{ + return OUTPUT_STATUS_UNKNOWN; +} + static Bool I830I2CDetectDVOControllers(ScrnInfoPtr pScrn, I2CBusPtr pI2CBus, struct _I830DVODriver **retdrv) @@ -189,6 +200,7 @@ i830_dvo_init(ScrnInfoPtr pScrn) pI830->output[i].mode_valid = i830_dvo_mode_valid; pI830->output[i].pre_set_mode = i830_dvo_pre_set_mode; pI830->output[i].post_set_mode = i830_dvo_post_set_mode; + pI830->output[i].detect = i830_dvo_detect; /* Set up the I2C and DDC buses */ ret = I830I2CInit(pScrn, &pI830->output[i].pI2CBus, GPIOE, "DVOI2C_E"); diff --git a/src/i830_lvds.c b/src/i830_lvds.c index 5b039b8e..e0e471ff 100644 --- a/src/i830_lvds.c +++ b/src/i830_lvds.c @@ -31,6 +31,7 @@ #include "xf86.h" #include "i830.h" +#include "i830_bios.h" /** * Sets the power state for the panel. @@ -176,6 +177,18 @@ i830_lvds_post_set_mode(ScrnInfoPtr pScrn, I830OutputPtr output, i830SetLVDSPanelPower(pScrn, TRUE); } +/** + * Detect the LVDS connection. + * + * This always returns OUTPUT_STATUS_CONNECTED. This output should only have + * been set up if the LVDS was actually connected anyway. + */ +static enum detect_status +i830_lvds_detect(ScrnInfoPtr pScrn, I830OutputPtr output) +{ + return OUTPUT_STATUS_CONNECTED; +} + void i830_lvds_init(ScrnInfoPtr pScrn) { @@ -221,6 +234,7 @@ i830_lvds_init(ScrnInfoPtr pScrn) pI830->output[pI830->num_outputs].mode_valid = i830_lvds_mode_valid; pI830->output[pI830->num_outputs].pre_set_mode = i830_lvds_pre_set_mode; pI830->output[pI830->num_outputs].post_set_mode = i830_lvds_post_set_mode; + pI830->output[pI830->num_outputs].detect = i830_lvds_detect; /* Set up the LVDS DDC channel. Most panels won't support it, but it can * be useful if available. diff --git a/src/i830_modes.c b/src/i830_modes.c index 633ac832..130b7fe5 100644 --- a/src/i830_modes.c +++ b/src/i830_modes.c @@ -803,17 +803,18 @@ I830ReprobePipeModeList(ScrnInfoPtr pScrn, int pipe) * is plugged in, then assume that it is. */ if (pI830->pipeMon[pipe] == NULL) { + enum detect_status detect; + + detect = pI830->output[output_index].detect(pScrn, + &pI830->output[output_index]); + switch (pI830->output[output_index].type) { case I830_OUTPUT_SDVO: - if (i830_sdvo_detect_displays(pScrn, &pI830->output[output_index])) + if (detect == OUTPUT_STATUS_CONNECTED) pI830->pipeMon[pipe] = i830GetConfiguredMonitor(pScrn); break; case I830_OUTPUT_ANALOG: - /* Do a disruptive detect if necessary, since we want to be sure we - * know if a monitor is attached, and this detect process should be - * infrequent. - */ - if (i830DetectCRT(pScrn, TRUE)) { + if (detect == OUTPUT_STATUS_CONNECTED) { /* if (pipe == pI830->pipe) pI830->pipeMon[pipe] = i830GetConfiguredMonitor(pScrn); else */ diff --git a/src/i830_sdvo.c b/src/i830_sdvo.c index d3f509e5..3932ea65 100644 --- a/src/i830_sdvo.c +++ b/src/i830_sdvo.c @@ -904,8 +904,8 @@ i830_sdvo_dump(ScrnInfoPtr pScrn) * * Takes 14ms on average on my i945G. */ -Bool -i830_sdvo_detect_displays(ScrnInfoPtr pScrn, I830OutputPtr output) +static enum detect_status +i830_sdvo_detect(ScrnInfoPtr pScrn, I830OutputPtr output) { CARD8 response[2]; CARD8 status; @@ -914,9 +914,12 @@ i830_sdvo_detect_displays(ScrnInfoPtr pScrn, I830OutputPtr output) status = i830_sdvo_read_response(output, &response, 2); if (status != SDVO_CMD_STATUS_SUCCESS) - return FALSE; + return OUTPUT_STATUS_UNKNOWN; - return (response[0] != 0 || response[1] != 0); + if (response[0] != 0 || response[1] != 0) + return OUTPUT_STATUS_CONNECTED; + else + return OUTPUT_STATUS_DISCONNECTED; } void @@ -936,6 +939,7 @@ i830_sdvo_init(ScrnInfoPtr pScrn, int output_device) output->mode_valid = i830_sdvo_mode_valid; output->pre_set_mode = i830_sdvo_pre_set_mode; output->post_set_mode = i830_sdvo_post_set_mode; + output->detect = i830_sdvo_detect; /* While it's the same bus, we just initialize a new copy to avoid trouble * with tracking refcounting ourselves, since the XFree86 DDX bits don't. diff --git a/src/i830_sdvo.h b/src/i830_sdvo.h index b1d86b41..1368e43b 100644 --- a/src/i830_sdvo.h +++ b/src/i830_sdvo.h @@ -31,8 +31,5 @@ i830_sdvo_init(ScrnInfoPtr pScrn, int output_device); int i830_sdvo_get_pixel_multiplier(DisplayModePtr pMode); -Bool -i830_sdvo_detect_displays(ScrnInfoPtr pScrn, I830OutputPtr output); - void i830_sdvo_dump(ScrnInfoPtr pScrn); diff --git a/src/i830_tv.c b/src/i830_tv.c index 2adbe91b..3e728822 100644 --- a/src/i830_tv.c +++ b/src/i830_tv.c @@ -405,6 +405,18 @@ i830_tv_post_set_mode(ScrnInfoPtr pScrn, I830OutputPtr output, OUTREG(TV_CTL, tv_ctl); } +/** + * Detect the TV connection. + * + * Currently this always returns OUTPUT_STATUS_UNKNOWN, as we need to be sure + * we have a pipe programmed in order to probe the TV. + */ +static enum detect_status +i830_tv_detect(ScrnInfoPtr pScrn, I830OutputPtr output) +{ + return OUTPUT_STATUS_UNKNOWN; +} + void i830_tv_init(ScrnInfoPtr pScrn) { @@ -425,6 +437,7 @@ i830_tv_init(ScrnInfoPtr pScrn) pI830->output[pI830->num_outputs].mode_valid = i830_tv_mode_valid; pI830->output[pI830->num_outputs].pre_set_mode = i830_tv_pre_set_mode; pI830->output[pI830->num_outputs].post_set_mode = i830_tv_post_set_mode; + pI830->output[pI830->num_outputs].detect = i830_tv_detect; pI830->num_outputs++; } |