diff --git a/src/i830_display.c b/src/i830_display.c index 7a8e96d..114ae44 100644 --- a/src/i830_display.c +++ b/src/i830_display.c @@ -1296,6 +1296,12 @@ i830_crtc_mode_set(xf86CrtcPtr crtc, DisplayModePtr mode, refclk = 48000; } + /* For SDVO TV, reference clock is input from SDVO encoder, + which will match pixel rate. Mode clock is within clock range + 100-200Mhz, and has been fixed up with possible muliply. */ + if (is_sdvo && is_tv) + refclk = adjusted_mode->Clock; + ok = i830FindBestPLL(crtc, adjusted_mode->Clock, refclk, &clock); if (!ok) FatalError("Couldn't find PLL settings for mode!\n"); @@ -1319,8 +1325,7 @@ i830_crtc_mode_set(xf86CrtcPtr crtc, DisplayModePtr mode, if (is_sdvo) { dpll |= DPLL_DVO_HIGH_SPEED; - if ((IS_I945G(pI830) || IS_I945GM(pI830) || IS_G33CLASS(pI830)) && - !is_tv) + if ((IS_I945G(pI830) || IS_I945GM(pI830) || IS_G33CLASS(pI830))) { int sdvo_pixel_multiply = adjusted_mode->Clock / mode->Clock; dpll |= (sdvo_pixel_multiply - 1) << SDVO_MULTIPLIER_SHIFT_HIRES; diff --git a/src/i830_sdvo.c b/src/i830_sdvo.c index 0750166..68a9e72 100644 --- a/src/i830_sdvo.c +++ b/src/i830_sdvo.c @@ -89,6 +89,7 @@ struct i830_sdvo_priv { */ struct i830_sdvo_sdtv_resolution_reply sdtv_resolutions; + DisplayModePtr tv_modes; /** * Current selected TV format. * @@ -594,9 +595,12 @@ i830_sdvo_create_preferred_input_timing(xf86OutputPtr output, uint16_t clock, struct i830_sdvo_preferred_input_timing_args args; uint8_t status; + memset(&args, 0, sizeof(args)); args.clock = clock; args.width = width; args.height = height; + args.interlace = 1; + args.scaled = 0; i830_sdvo_write_cmd(output, SDVO_CMD_CREATE_PREFERRED_INPUT_TIMING, &args, sizeof(args)); status = i830_sdvo_read_response(output, NULL, 0); @@ -702,13 +706,13 @@ i830_sdvo_get_dtd_from_mode(struct i830_sdvo_dtd *dtd, DisplayModePtr mode) dtd->part1.v_high = (((height >> 8) & 0xf) << 4) | ((v_blank_len >> 8) & 0xf); - dtd->part2.h_sync_off = h_sync_offset; + dtd->part2.h_sync_off = h_sync_offset & 0xff; dtd->part2.h_sync_width = h_sync_len & 0xff; dtd->part2.v_sync_off_width = (v_sync_offset & 0xf) << 4 | (v_sync_len & 0xf); dtd->part2.sync_off_width_high = ((h_sync_offset & 0x300) >> 2) | ((h_sync_len & 0x300) >> 4) | ((v_sync_offset & 0x30) >> 2) | - ((v_sync_len & 0x30) >> 4); + ((v_sync_len & 0x300) >> 8); dtd->part2.dtd_flags = 0x18; if (mode->Flags & V_PHSYNC) @@ -724,27 +728,10 @@ i830_sdvo_get_dtd_from_mode(struct i830_sdvo_dtd *dtd, DisplayModePtr mode) static void i830_sdvo_get_mode_from_dtd(DisplayModePtr mode, struct i830_sdvo_dtd *dtd) { - uint16_t width, height; - uint16_t h_blank_len, h_sync_len, v_blank_len, v_sync_len; - uint16_t h_sync_offset, v_sync_offset; - - width = mode->CrtcHDisplay; - height = mode->CrtcVDisplay; - - /* do some mode translations */ - h_blank_len = mode->CrtcHBlankEnd - mode->CrtcHBlankStart; - h_sync_len = mode->CrtcHSyncEnd - mode->CrtcHSyncStart; - - v_blank_len = mode->CrtcVBlankEnd - mode->CrtcVBlankStart; - v_sync_len = mode->CrtcVSyncEnd - mode->CrtcVSyncStart; - - h_sync_offset = mode->CrtcHSyncStart - mode->CrtcHBlankStart; - v_sync_offset = mode->CrtcVSyncStart - mode->CrtcVBlankStart; - mode->HDisplay = dtd->part1.h_active; mode->HDisplay += ((dtd->part1.h_high >> 4) & 0x0f) << 8; mode->HSyncStart = mode->HDisplay + dtd->part2.h_sync_off; - mode->HSyncStart += (dtd->part2.sync_off_width_high & 0xa0) << 2; + mode->HSyncStart += (dtd->part2.sync_off_width_high & 0xc0) << 2; mode->HSyncEnd = mode->HSyncStart + dtd->part2.h_sync_width; mode->HSyncEnd += (dtd->part2.sync_off_width_high & 0x30) << 4; mode->HTotal = mode->HDisplay + dtd->part1.h_blank; @@ -754,7 +741,7 @@ i830_sdvo_get_mode_from_dtd(DisplayModePtr mode, struct i830_sdvo_dtd *dtd) mode->VDisplay += ((dtd->part1.v_high >> 4) & 0x0f) << 8; mode->VSyncStart = mode->VDisplay; mode->VSyncStart += (dtd->part2.v_sync_off_width >> 4) & 0xf; - mode->VSyncStart += (dtd->part2.sync_off_width_high & 0x0a) << 2; + mode->VSyncStart += (dtd->part2.sync_off_width_high & 0x0c) << 2; mode->VSyncStart += dtd->part2.v_sync_off_high & 0xc0; mode->VSyncEnd = mode->VSyncStart + (dtd->part2.v_sync_off_width & 0xf); mode->VSyncEnd += (dtd->part2.sync_off_width_high & 0x3) << 4; @@ -763,7 +750,7 @@ i830_sdvo_get_mode_from_dtd(DisplayModePtr mode, struct i830_sdvo_dtd *dtd) mode->Clock = dtd->part1.clock * 10; - mode->Flags &= (V_PHSYNC | V_PVSYNC); + mode->Flags &= ~(V_PHSYNC | V_PVSYNC); if (dtd->part2.dtd_flags & 0x2) mode->Flags |= V_PHSYNC; if (dtd->part2.dtd_flags & 0x4) @@ -953,6 +940,30 @@ static void i830_sdvo_set_avi_infoframe(xf86OutputPtr output, SDVO_HBUF_TX_VSYNC); } +static void +i830_sdvo_set_tv_format(xf86OutputPtr output) +{ + ScrnInfoPtr pScrn = output->scrn; + I830OutputPrivatePtr intel_output = output->driver_private; + struct i830_sdvo_priv *dev_priv = intel_output->dev_priv; + struct i830_sdvo_tv_format *format, unset; + uint8_t status; + + format = &dev_priv->tv_format; + memset(&unset, 0, sizeof(unset)); + if (memcmp(format, &unset, sizeof(*format))) { + xf86DrvMsg(pScrn->scrnIndex, X_INFO, + "%s: Choosing default TV format of NTSC-M\n", + SDVO_NAME(dev_priv)); + format->ntsc_m = 1; + i830_sdvo_write_cmd(output, SDVO_CMD_SET_TV_FORMAT, format, + sizeof(*format)); + status = i830_sdvo_read_response(output, NULL, 0); + if (status != SDVO_CMD_STATUS_SUCCESS) + ErrorF("zhen: set TV format failed\n"); + } +} + static Bool i830_sdvo_mode_fixup(xf86OutputPtr output, DisplayModePtr mode, DisplayModePtr adjusted_mode) @@ -998,8 +1009,12 @@ i830_sdvo_mode_fixup(xf86OutputPtr output, DisplayModePtr mode, i830_sdvo_get_mode_from_dtd(adjusted_mode, &input_dtd); + xf86SetModeCrtc(adjusted_mode, 0); + ErrorF("input modeline:\n"); xf86PrintModeline(0, adjusted_mode); + /* Clock range is required to be in 100-200Mhz */ + adjusted_mode->Clock *= i830_sdvo_get_pixel_multiplier(adjusted_mode); } else { return FALSE; } @@ -1045,30 +1060,38 @@ i830_sdvo_mode_set(xf86OutputPtr output, DisplayModePtr mode, sdvox |= SDVO_AUDIO_ENABLE; } - i830_sdvo_get_dtd_from_mode(&input_dtd, mode); + /* We have tried to get input timing in mode_fixup, and filled into + adjusted_mode */ + if (dev_priv->is_tv) + i830_sdvo_get_dtd_from_mode(&input_dtd, adjusted_mode); + else + i830_sdvo_get_dtd_from_mode(&input_dtd, mode); /* If it's a TV, we already set the output timing in mode_fixup. * Otherwise, the output timing is equal to the input timing. */ + i830_sdvo_set_target_output(output, dev_priv->controlled_output); + /* Set the input timing to the screen. Assume always input 0. */ + i830_sdvo_set_target_input(output, TRUE, FALSE); + + if (dev_priv->is_tv) + i830_sdvo_set_tv_format(output); + if (!dev_priv->is_tv) { /* Set the output timing to the screen */ - i830_sdvo_set_target_output(output, dev_priv->controlled_output); i830_sdvo_set_output_timing(output, &input_dtd); } - /* Set the input timing to the screen. Assume always input 0. */ - i830_sdvo_set_target_input(output, TRUE, FALSE); - /* We would like to use i830_sdvo_create_preferred_input_timing() to * provide the device with a timing it can support, if it supports that * feature. However, presumably we would need to adjust the CRTC to output * the preferred timing, and we don't support that currently. */ #if 0 - success = i830_sdvo_create_preferred_input_timing(output, clock, - width, height); + success = i830_sdvo_create_preferred_input_timing(output, + mode->Clock / 10, mode->HDisplay, mode->VDisplay); if (success) { - struct i830_sdvo_dtd *input_dtd; + struct i830_sdvo_dtd input_dtd; i830_sdvo_get_preferred_input_timing(output, &input_dtd); i830_sdvo_set_input_timing(output, &input_dtd); @@ -1273,6 +1296,33 @@ i830_sdvo_mode_valid(xf86OutputPtr output, DisplayModePtr pMode) if (dev_priv->pixel_clock_max < pMode->Clock) return MODE_CLOCK_HIGH; + /* check TV resolution, SDTV for now */ + if (dev_priv->is_tv) { + struct i830_sdvo_sdtv_resolution_reply *res = &dev_priv->sdtv_resolutions; + DisplayModePtr tv_modes = dev_priv->tv_modes; + Bool match = FALSE; + + if (pMode->HDisplay > 1280) + return MODE_VIRTUAL_X; + if (pMode->VDisplay > 1024) + return MODE_VIRTUAL_Y; + if (pMode->HDisplay > 1280 && res->res_1280x1024 == 0) + return MODE_VIRTUAL_X; + if (pMode->VDisplay > 1024 && res->res_1280x1024 == 0) + return MODE_VIRTUAL_Y; + + while (tv_modes) { + if (pMode->HDisplay == tv_modes->HDisplay && + pMode->VDisplay == tv_modes->VDisplay) { + match = TRUE; + break; + } + tv_modes = tv_modes->next; + } + if (!match) + return MODE_BAD; + } + return MODE_OK; } @@ -1556,10 +1606,9 @@ i830_sdvo_get_tv_mode(DisplayModePtr *head, int width, int height, static void i830_sdvo_check_tv_format(xf86OutputPtr output) { - ScrnInfoPtr pScrn = output->scrn; I830OutputPrivatePtr intel_output = output->driver_private; struct i830_sdvo_priv *dev_priv = intel_output->dev_priv; - struct i830_sdvo_tv_format format, unset; + struct i830_sdvo_tv_format format; uint8_t status; i830_sdvo_write_cmd(output, SDVO_CMD_GET_TV_FORMAT, NULL, 0); @@ -1567,16 +1616,7 @@ i830_sdvo_check_tv_format(xf86OutputPtr output) if (status != SDVO_CMD_STATUS_SUCCESS) return; - memset(&unset, 0, sizeof(unset)); - if (memcmp(&format, &unset, sizeof(format))) { - xf86DrvMsg(pScrn->scrnIndex, X_INFO, - "%s: Choosing default TV format of NTSC-M\n", - SDVO_NAME(dev_priv)); - - format.ntsc_m = TRUE; - i830_sdvo_write_cmd(output, SDVO_CMD_SET_TV_FORMAT, NULL, 0); - status = i830_sdvo_read_response(output, NULL, 0); - } + memcpy(&dev_priv->tv_format, &format, sizeof(format)); } static DisplayModePtr @@ -1586,6 +1626,7 @@ i830_sdvo_get_tv_modes(xf86OutputPtr output) struct i830_sdvo_priv *dev_priv = intel_output->dev_priv; DisplayModePtr modes = NULL; struct i830_sdvo_sdtv_resolution_reply *res = &dev_priv->sdtv_resolutions; + struct i830_sdvo_sdtv_resolution_request tv_res; uint8_t status; float refresh = 60; /* XXX */ @@ -1593,7 +1634,10 @@ i830_sdvo_get_tv_modes(xf86OutputPtr output) /* Read the list of supported input resolutions for the selected TV format. */ - i830_sdvo_write_cmd(output, SDVO_CMD_GET_SDTV_RESOLUTION_SUPPORT, NULL, 0); + memset(&tv_res, 0, sizeof(tv_res)); + memcpy(&tv_res, &dev_priv->tv_format, sizeof(tv_res)); + i830_sdvo_write_cmd(output, SDVO_CMD_GET_SDTV_RESOLUTION_SUPPORT, + &tv_res, sizeof(tv_res)); status = i830_sdvo_read_response(output, res, sizeof(*res)); if (status != SDVO_CMD_STATUS_SUCCESS) return NULL; @@ -1617,6 +1661,8 @@ i830_sdvo_get_tv_modes(xf86OutputPtr output) if (res->res_1024x768) i830_sdvo_get_tv_mode(&modes, 1024, 768, refresh); if (res->res_1280x1024) i830_sdvo_get_tv_mode(&modes, 1280, 1024, refresh); + dev_priv->tv_modes = xf86DuplicateModes(output->scrn, modes); + return modes; } diff --git a/src/i830_sdvo_regs.h b/src/i830_sdvo_regs.h index 6988d49..ab38355 100644 --- a/src/i830_sdvo_regs.h +++ b/src/i830_sdvo_regs.h @@ -101,6 +101,9 @@ struct i830_sdvo_preferred_input_timing_args { uint16_t clock; uint16_t width; uint16_t height; + uint8_t interlace:1; + uint8_t scaled:1; + uint8_t pad:6; } __attribute__((packed)); /* I2C registers for SDVO */