diff options
author | Keith Packard <keithp@neko.keithp.com> | 2006-10-24 16:53:46 -0700 |
---|---|---|
committer | Keith Packard <keithp@neko.keithp.com> | 2006-10-24 16:53:46 -0700 |
commit | cd9c6e29146e1debaba4b0b9ad0d241f07bdbc14 (patch) | |
tree | db76e867c3190af4cd6b3051f94d30b50377fb3e | |
parent | c5cca4c20ae6b519e3b021a9d90809c1b3d1facb (diff) | |
parent | eec5580cefffc293bf547372ab63b2fedaef4a83 (diff) |
Merge branch 'modesetting-origin' into modesetting
-rw-r--r-- | src/Makefile.am | 3 | ||||
-rw-r--r-- | src/i810_reg.h | 563 | ||||
-rw-r--r-- | src/i830.h | 70 | ||||
-rw-r--r-- | src/i830_crt.c | 133 | ||||
-rw-r--r-- | src/i830_display.c | 626 | ||||
-rw-r--r-- | src/i830_display.h | 4 | ||||
-rw-r--r-- | src/i830_driver.c | 266 | ||||
-rw-r--r-- | src/i830_dvo.c | 136 | ||||
-rw-r--r-- | src/i830_lvds.c | 199 | ||||
-rw-r--r-- | src/i830_modes.c | 8 | ||||
-rw-r--r-- | src/i830_sdvo.c | 333 | ||||
-rw-r--r-- | src/i830_sdvo.h | 9 | ||||
-rw-r--r-- | src/i830_tv.c | 430 |
13 files changed, 2067 insertions, 713 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 7af2dd7e..0fce5e4f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -53,6 +53,7 @@ i810_drv_la_SOURCES = \ i830_bios.c \ i830_bios.h \ i830_common.h \ + i830_crt.c \ i830_cursor.c \ i830_debug.c \ i830_debug.h \ @@ -65,6 +66,7 @@ i810_drv_la_SOURCES = \ i830_gtf.c \ i830_i2c.c \ i830_io.c \ + i830_lvds.c \ i830_memory.c \ i830_modes.c \ i830_video.c \ @@ -75,6 +77,7 @@ i810_drv_la_SOURCES = \ i830_sdvo.c \ i830_sdvo.h \ i830_sdvo_regs.h \ + i830_tv.c \ i830_xf86Modes.h \ i830_xf86Modes.c \ i915_3d.c \ diff --git a/src/i810_reg.h b/src/i810_reg.h index 4fec65ff..a80b66ee 100644 --- a/src/i810_reg.h +++ b/src/i810_reg.h @@ -26,9 +26,14 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. **************************************************************************/ +/** @file + * Register names and fields for Intel graphics. + */ + /* * Authors: * Keith Whitwell <keith@tungstengraphics.com> + * Eric Anholt <eric@anholt.net> * * based on the i740 driver by * Kevin E. Martin <kevin@precisioninsight.com> @@ -928,6 +933,564 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # define LVDS_CLKA_POWER_DOWN (0 << 8) # define LVDS_CLKA_POWER_UP (3 << 8) +/** @defgroup TV_CTL + * @{ + */ +#define TV_CTL 0x68000 +/** Enables the TV encoder */ +# define TV_ENC_ENABLE (1 << 31) +/** Sources the TV encoder input from pipe B instead of A. */ +# define TV_ENC_PIPEB_SELECT (1 << 30) +/** Outputs composite video (DAC A only) */ +# define TV_ENC_OUTPUT_COMPOSITE (0 << 28) +/** Outputs SVideo video (DAC B/C) */ +# define TV_ENC_OUTPUT_SVIDEO (1 << 28) +/** Outputs Component video (DAC A/B/C) */ +# define TV_ENC_OUTPUT_COMPONENT (2 << 28) +# define TV_TRILEVEL_SYNC (1 << 21) +/** Enables slow sync generation (945GM only) */ +# define TV_SLOW_SYNC (1 << 20) +/** Selects 4x oversampling for 480i and 576p */ +# define TV_OVERSAMPLE_4X (0 << 18) +/** Selects 2x oversampling for 720p and 1080i */ +# define TV_OVERSAMPLE_2X (1 << 18) +/** Selects no oversampling for 1080p */ +# define TV_OVERSAMPLE_NONE (2 << 18) +/** Selects 8x oversampling */ +# define TV_OVERSAMPLE_8X (3 << 18) +/** Selects progressive mode rather than interlaced */ +# define TV_PROGRESSIVE (1 << 17) +/** Sets the colorburst to PAL mode. Required for non-M PAL modes. */ +# define TV_PAL_BURST (1 << 16) +/** Field for setting delay of Y compared to C */ +# define TV_YC_SKEW_MASK (7 << 12) +/** Enables a fix for 480p/576p standard definition modes on the 915GM only */ +# define TV_ENC_SDP_FIX (1 << 11) +/** + * Enables a fix for the 915GM only. + * + * Not sure what it does. + */ +# define TV_ENC_C0_FIX (1 << 10) +/** Bits that must be preserved by software */ +# define TV_CTL_SAVE ((3 << 8) | (3 << 6)) +# define TV_FUSE_STATE_MASK (3 << 4) +/** Read-only state that reports all features enabled */ +# define TV_FUSE_STATE_ENABLED (0 << 4) +/** Read-only state that reports that Macrovision is disabled in hardware*/ +# define TV_FUSE_STATE_NO_MACROVISION (1 << 4) +/** Read-only state that reports that TV-out is disabled in hardware. */ +# define TV_FUSE_STATE_DISABLED (2 << 4) +/** + * This test mode forces the DACs to 50% of full output. + * + * This is used for load detection in combination with TVDAC_SENSE_MASK + */ +# define TV_TEST_MODE_MONITOR_DETECT (7 << 0) +/** @} */ + +/** @defgroup TV_DAC + * @{ + */ +#define TV_DAC 0x68004 +/** + * Reports that DAC state change logic has reported change (RO). + * + * This gets cleared when TV_DAC_STATE_EN is cleared +*/ +# define TVDAC_STATE_CHG (1 << 31) +# define TVDAC_SENSE_MASK (7 << 28) +/** Reports that DAC A voltage is above the detect threshold */ +# define TVDAC_A_SENSE (1 << 30) +/** Reports that DAC B voltage is above the detect threshold */ +# define TVDAC_B_SENSE (1 << 29) +/** Reports that DAC C voltage is above the detect threshold */ +# define TVDAC_C_SENSE (1 << 28) +/** + * Enables DAC state detection logic, for load-based TV detection. + * + * The PLL of the chosen pipe (in TV_CTL) must be running, and the encoder set + * to off, for load detection to work. + */ +# define TVDAC_STATE_CHG_EN (1 << 27) +/** Sets the DAC A sense value to high */ +# define TVDAC_A_SENSE_CTL (1 << 26) +/** Sets the DAC B sense value to high */ +# define TVDAC_B_SENSE_CTL (1 << 25) +/** Sets the DAC C sense value to high */ +# define TVDAC_C_SENSE_CTL (1 << 24) +/** Overrides the ENC_ENABLE and DAC voltage levels */ +# define DAC_CTL_OVERRIDE (1 << 7) +/** Sets the slew rate. Must be preserved in software */ +# define ENC_TVDAC_SLEW_FAST (1 << 6) +# define DAC_A_1_3_V (0 << 4) +# define DAC_A_1_1_V (1 << 4) +# define DAC_A_0_7_V (2 << 4) +# define DAC_A_OFF (3 << 4) +# define DAC_B_1_3_V (0 << 2) +# define DAC_B_1_1_V (1 << 2) +# define DAC_B_0_7_V (2 << 2) +# define DAC_B_OFF (3 << 2) +# define DAC_C_1_3_V (0 << 0) +# define DAC_C_1_1_V (1 << 0) +# define DAC_C_0_7_V (2 << 0) +# define DAC_C_OFF (3 << 0) +/** @} */ + +/** + * CSC coefficients are stored in a floating point format with 9 bits of + * mantissa and 2 or 3 bits of exponent. The exponent is represented as 2**-n, + * where 2-bit exponents are unsigned n, and 3-bit exponents are signed n with + * -1 (0x3) being the only legal negative value. + */ +#define TV_CSC_Y 0x68010 +# define TV_RY_MASK 0x07ff0000 +# define TV_RY_SHIFT 16 +# define TV_GY_MASK 0x00000fff +# define TV_GY_SHIFT 0 + +#define TV_CSC_Y2 0x68014 +# define TV_BY_MASK 0x07ff0000 +# define TV_BY_SHIFT 16 +/** + * Y attenuation for component video. + * + * Stored in 1.9 fixed point. + */ +# define TV_AY_MASK 0x000003ff +# define TV_AY_SHIFT 0 + +#define TV_CSC_U 0x68018 +# define TV_RU_MASK 0x07ff0000 +# define TV_RU_SHIFT 16 +# define TV_GU_MASK 0x000007ff +# define TV_GU_SHIFT 0 + +#define TV_CSC_U2 0x6801c +# define TV_BU_MASK 0x07ff0000 +# define TV_BU_SHIFT 16 +/** + * U attenuation for component video. + * + * Stored in 1.9 fixed point. + */ +# define TV_AU_MASK 0x000003ff +# define TV_AU_SHIFT 0 + +#define TV_CSC_V 0x68020 +# define TV_RV_MASK 0x0fff0000 +# define TV_RV_SHIFT 16 +# define TV_GV_MASK 0x000007ff +# define TV_GV_SHIFT 0 + +#define TV_CSC_V2 0x68024 +# define TV_BV_MASK 0x07ff0000 +# define TV_BV_SHIFT 16 +/** + * V attenuation for component video. + * + * Stored in 1.9 fixed point. + */ +# define TV_AV_MASK 0x000007ff +# define TV_AV_SHIFT 0 + +/** @defgroup TV_CSC_KNOBS + * @{ + */ +#define TV_CLR_KNOBS 0x68028 +/** 2s-complement brightness adjustment */ +# define TV_BRIGHTNESS_MASK 0xff000000 +# define TV_BRIGHTNESS_SHIFT 24 +/** Contrast adjustment, as a 2.6 unsigned floating point number */ +# define TV_CONTRAST_MASK 0x00ff0000 +# define TV_CONTRAST_SHIFT 16 +/** Saturation adjustment, as a 2.6 unsigned floating point number */ +# define TV_SATURATION_MASK 0x0000ff00 +# define TV_SATURATION_SHIFT 8 +/** Hue adjustment, as an integer phase angle in degrees */ +# define TV_HUE_MASK 0x000000ff +# define TV_HUE_SHIFT 0 +/** @} */ + +/** @defgroup TV_CLR_LEVEL + * @{ + */ +#define TV_CLR_LEVEL 0x6802c +/** Controls the DAC level for black */ +# define TV_BLACK_LEVEL_MASK 0x01ff0000 +# define TV_BLACK_LEVEL_SHIFT 16 +/** Controls the DAC level for blanking */ +# define TV_BLANK_LEVEL_MASK 0x000001ff +# define TV_BLANK_LEVEL_SHIFT 0 +/* @} */ + +/** @defgroup TV_H_CTL_1 + * @{ + */ +#define TV_H_CTL_1 0x68030 +/** Number of pixels in the hsync. */ +# define TV_HSYNC_END_MASK 0x1fff0000 +# define TV_HSYNC_END_SHIFT 16 +/** Total number of pixels minus one in the line (display and blanking). */ +# define TV_HTOTAL_MASK 0x00001fff +# define TV_HTOTAL_SHIFT 0 +/** @} */ + +/** @defgroup TV_H_CTL_2 + * @{ + */ +#define TV_H_CTL_2 0x68034 +/** Enables the colorburst (needed for non-component color) */ +# define TV_BURST_ENA (1 << 31) +/** Offset of the colorburst from the start of hsync, in pixels minus one. */ +# define TV_HBURST_START_SHIFT 16 +# define TV_HBURST_START_MASK 0x1fff0000 +/** Length of the colorburst */ +# define TV_HBURST_LEN_SHIFT 0 +# define TV_HBURST_LEN_MASK 0x0001fff +/** @} */ + +/** @defgroup TV_H_CTL_3 + * @{ + */ +#define TV_H_CTL_3 0x68038 +/** End of hblank, measured in pixels minus one from start of hsync */ +# define TV_HBLANK_END_SHIFT 16 +# define TV_HBLANK_END_MASK 0x1fff0000 +/** Start of hblank, measured in pixels minus one from start of hsync */ +# define TV_HBLANK_START_SHIFT 0 +# define TV_HBLANK_START_MASK 0x0001fff +/** @} */ + +/** @defgroup TV_V_CTL_1 + * @{ + */ +#define TV_V_CTL_1 0x6803c +/** XXX */ +# define TV_NBR_END_SHIFT 16 +# define TV_NBR_END_MASK 0x07ff0000 +/** XXX */ +# define TV_VI_END_F1_SHIFT 8 +# define TV_VI_END_F1_MASK 0x00003f00 +/** XXX */ +# define TV_VI_END_F2_SHIFT 0 +# define TV_VI_END_F2_MASK 0x0000003f +/** @} */ + +/** @defgroup TV_V_CTL_2 + * @{ + */ +#define TV_V_CTL_2 0x68040 +/** Length of vsync, in half lines */ +# define TV_VSYNC_LEN_MASK 0x07ff0000 +# define TV_VSYNC_LEN_SHIFT 16 +/** Offset of the start of vsync in field 1, measured in one less than the + * number of half lines. + */ +# define TV_VSYNC_START_F1_MASK 0x00007f00 +# define TV_VSYNC_START_F1_SHIFT 8 +/** + * Offset of the start of vsync in field 2, measured in one less than the + * number of half lines. + */ +# define TV_VSYNC_START_F2_MASK 0x0000007f +# define TV_VSYNC_START_F2_SHIFT 0 +/** @} */ + +/** @defgroup TV_V_CTL_3 + * @{ + */ +#define TV_V_CTL_3 0x68044 +/** Enables generation of the equalization signal */ +# define TV_EQUAL_ENA (1 << 31) +/** Length of vsync, in half lines */ +# define TV_VEQ_LEN_MASK 0x007f0000 +# define TV_VEQ_LEN_SHIFT 16 +/** Offset of the start of equalization in field 1, measured in one less than + * the number of half lines. + */ +# define TV_VEQ_START_F1_MASK 0x0007f00 +# define TV_VEQ_START_F1_SHIFT 8 +/** + * Offset of the start of equalization in field 2, measured in one less than + * the number of half lines. + */ +# define TV_VEQ_START_F2_MASK 0x000007f +# define TV_VEQ_START_F2_SHIFT 0 +/** @} */ + +/** @defgroup TV_V_CTL_4 + * @{ + */ +#define TV_V_CTL_4 0x68048 +/** + * Offset to start of vertical colorburst, measured in one less than the + * number of lines from vertical start. + */ +# define TV_VBURST_START_F1_MASK 0x003f0000 +# define TV_VBURST_START_F1_SHIFT 16 +/** + * Offset to the end of vertical colorburst, measured in one less than the + * number of lines from the start of NBR. + */ +# define TV_VBURST_END_F1_MASK 0x000000ff +# define TV_VBURST_END_F1_SHIFT 0 +/** @} */ + +/** @defgroup TV_V_CTL_5 + * @{ + */ +#define TV_V_CTL_5 0x6804c +/** + * Offset to start of vertical colorburst, measured in one less than the + * number of lines from vertical start. + */ +# define TV_VBURST_START_F2_MASK 0x003f0000 +# define TV_VBURST_START_F2_SHIFT 16 +/** + * Offset to the end of vertical colorburst, measured in one less than the + * number of lines from the start of NBR. + */ +# define TV_VBURST_END_F2_MASK 0x000000ff +# define TV_VBURST_END_F2_SHIFT 0 +/** @} */ + +/** @defgroup TV_V_CTL_6 + * @{ + */ +#define TV_V_CTL_6 0x68050 +/** + * Offset to start of vertical colorburst, measured in one less than the + * number of lines from vertical start. + */ +# define TV_VBURST_START_F3_MASK 0x003f0000 +# define TV_VBURST_START_F3_SHIFT 16 +/** + * Offset to the end of vertical colorburst, measured in one less than the + * number of lines from the start of NBR. + */ +# define TV_VBURST_END_F3_MASK 0x000000ff +# define TV_VBURST_END_F3_SHIFT 0 +/** @} */ + +/** @defgroup TV_V_CTL_7 + * @{ + */ +#define TV_V_CTL_7 0x68054 +/** + * Offset to start of vertical colorburst, measured in one less than the + * number of lines from vertical start. + */ +# define TV_VBURST_START_F4_MASK 0x003f0000 +# define TV_VBURST_START_F4_SHIFT 16 +/** + * Offset to the end of vertical colorburst, measured in one less than the + * number of lines from the start of NBR. + */ +# define TV_VBURST_END_F4_MASK 0x000000ff +# define TV_VBURST_END_F4_SHIFT 0 +/** @} */ + +/** @defgroup TV_SC_CTL_1 + * @{ + */ +#define TV_SC_CTL_1 0x68060 +/** Turns on the first subcarrier phase generation DDA */ +# define TV_SC_DDA1_EN (1 << 31) +/** Turns on the first subcarrier phase generation DDA */ +# define TV_SC_DDA2_EN (2 << 31) +/** Turns on the first subcarrier phase generation DDA */ +# define TV_SC_DDA3_EN (3 << 31) +/** Sets the subcarrier DDA to reset frequency every other field */ +# define TV_SC_RESET_EVERY_2 (0 << 24) +/** Sets the subcarrier DDA to reset frequency every fourth field */ +# define TV_SC_RESET_EVERY_4 (1 << 24) +/** Sets the subcarrier DDA to reset frequency every eighth field */ +# define TV_SC_RESET_EVERY_8 (2 << 24) +/** Sets the subcarrier DDA to never reset the frequency */ +# define TV_SC_RESET_NEVER (3 << 24) +/** Sets the peak amplitude of the colorburst.*/ +# define TV_BURST_LEVEL_MASK 0x00ff0000 +# define TV_BURST_LEVEL_SHIFT 16 +/** Sets the increment of the first subcarrier phase generation DDA */ +# define TV_SCDDA1_INC_MASK 0x00000fff +# define TV_SCDDA1_INC_SHIFT 0 +/** @} */ + +/** @defgroup TV_SC_CTL_2 + * @{ + */ +#define TV_SC_CTL_2 0x68068 +/** Sets the rollover for the second subcarrier phase generation DDA */ +# define TV_SCDDA2_SIZE_MASK 0x7fff0000 +# define TV_SCDDA2_SIZE_SHIFT 16 +/** Sets the increent of the second subcarrier phase generation DDA */ +# define TV_SCDDA2_INC_MASK 0x00007fff +# define TV_SCDDA2_INC_SHIFT 0 +/** @} */ + +/** @defgroup TV_SC_CTL_3 + * @{ + */ +#define TV_SC_CTL_3 0x68068 +/** Sets the rollover for the third subcarrier phase generation DDA */ +# define TV_SCDDA3_SIZE_MASK 0x7fff0000 +# define TV_SCDDA3_SIZE_SHIFT 16 +/** Sets the increent of the third subcarrier phase generation DDA */ +# define TV_SCDDA3_INC_MASK 0x00007fff +# define TV_SCDDA3_INC_SHIFT 0 +/** @} */ + +/** @defgroup TV_WIN_POS + * @{ + */ +#define TV_WIN_POS 0x68070 +/** X coordinate of the display from the start of horizontal active */ +# define TV_XPOS_MASK 0x1fff0000 +# define TV_XPOS_SHIFT 16 +/** Y coordinate of the display from the start of vertical active (NBR) */ +# define TV_YPOS_MASK 0x00000fff +# define TV_YPOS_SHIFT 0 +/** @} */ + +/** @defgroup TV_WIN_SIZE + * @{ + */ +#define TV_WIN_SIZE 0x68074 +/** Horizontal size of the display window, measured in pixels*/ +# define TV_XSIZE_MASK 0x1fff0000 +# define TV_XSIZE_SHIFT 16 +/** + * Vertical size of the display window, measured in pixels. + * + * Must be even for interlaced modes. + */ +# define TV_YSIZE_MASK 0x00000fff +# define TV_YSIZE_SHIFT 0 +/** @} */ + +/** @defgroup TV_FILTER_CTL_1 + * @{ + */ +#define TV_FILTER_CTL_1 0x68080 +/** + * Enables automatic scaling calculation. + * + * If set, the rest of the registers are ignored, and the calculated values can + * be read back from the register. + */ +# define TV_AUTO_SCALE (1 << 31) +/** + * Disables the vertical filter. + * + * This is required on modes more than 1024 pixels wide */ +# define TV_V_FILTER_BYPASS (1 << 29) +/** Enables adaptive vertical filtering */ +# define TV_VADAPT (1 << 28) +# define TV_VADAPT_MODE_MASK (3 << 26) +/** Selects the least adaptive vertical filtering mode */ +# define TV_VADAPT_MODE_LEAST (0 << 26) +/** Selects the moderately adaptive vertical filtering mode */ +# define TV_VADAPT_MODE_MODERATE (1 << 26) +/** Selects the most adaptive vertical filtering mode */ +# define TV_VADAPT_MODE_MOST (3 << 26) +/** + * Sets the horizontal scaling factor. + * + * This should be the fractional part of the horizontal scaling factor divided + * by the oversampling rate. TV_HSCALE should be less than 1, and set to: + * + * (src width - 1) / ((oversample * dest width) - 1) + */ +# define TV_HSCALE_FRAC_MASK 0x00003fff +# define TV_HSCALE_FRAC_SHIFT 0 +/** @} */ + +/** @defgroup TV_FILTER_CTL_2 + * @{ + */ +#define TV_FILTER_CTL_2 0x68084 +/** + * Sets the integer part of the 3.15 fixed-point vertical scaling factor. + * + * TV_VSCALE should be (src height - 1) / ((interlace * dest height) - 1) + */ +# define TV_VSCALE_INT_MASK 0x00038000 +# define TV_VSCALE_INT_SHIFT 15 +/** + * Sets the fractional part of the 3.15 fixed-point vertical scaling factor. + * + * \sa TV_VSCALE_INT_MASK + */ +# define TV_VSCALE_FRAC_MASK 0x00007fff +# define TV_VSCALE_FRAC_SHIFT 0 +/** @} */ + +/** @defgroup TV_FILTER_CTL_3 + * @{ + */ +#define TV_FILTER_CTL_3 0x68088 +/** + * Sets the integer part of the 3.15 fixed-point vertical scaling factor. + * + * TV_VSCALE should be (src height - 1) / (1/4 * (dest height - 1)) + * + * For progressive modes, TV_VSCALE_IP_INT should be set to zeroes. + */ +# define TV_VSCALE_IP_INT_MASK 0x00038000 +# define TV_VSCALE_IP_INT_SHIFT 15 +/** + * Sets the fractional part of the 3.15 fixed-point vertical scaling factor. + * + * For progressive modes, TV_VSCALE_IP_INT should be set to zeroes. + * + * \sa TV_VSCALE_IP_INT_MASK + */ +# define TV_VSCALE_IP_FRAC_MASK 0x00007fff +# define TV_VSCALE_IP_FRAC_SHIFT 0 +/** @} */ + +/** @defgroup TV_CC_CONTROL + * @{ + */ +#define TV_CC_CONTROL 0x68090 +# define TV_CC_ENABLE (1 << 31) +/** + * Specifies which field to send the CC data in. + * + * CC data is usually sent in field 0. + */ +# define TV_CC_FID_MASK (1 << 27) +# define TV_CC_FID_SHIFT 27 +/** Sets the horizontal position of the CC data. Usually 135. */ +# define TV_CC_HOFF_MASK 0x03ff0000 +# define TV_CC_HOFF_SHIFT 16 +/** Sets the vertical position of the CC data. Usually 21 */ +# define TV_CC_LINE_MASK 0x0000003f +# define TV_CC_LINE_SHIFT 0 +/** @} */ + +/** @defgroup TV_CC_DATA + * @{ + */ +#define TV_CC_DATA 0x68094 +# define TV_CC_RDY (1 << 31) +/** Second word of CC data to be transmitted. */ +# define TV_CC_DATA_2_MASK 0x007f0000 +# define TV_CC_DATA_2_SHIFT 16 +/** First word of CC data to be transmitted. */ +# define TV_CC_DATA_1_MASK 0x0000007f +# define TV_CC_DATA_1_SHIFT 0 +/** @} + */ + +/** @{ */ +#define TV_H_LUMA_0 0x68100 +#define TV_H_LUMA_59 0x681ec +#define TV_H_CHROMA_0 0x68200 +#define TV_H_CHROMA_59 0x682ec +/** @} */ + #define PIPEACONF 0x70008 #define PIPEACONF_ENABLE (1<<31) #define PIPEACONF_DISABLE 0 @@ -69,6 +69,8 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "i830_dri.h" #endif +typedef struct _I830OutputRec I830OutputRec, *I830OutputPtr; + #include "common.h" #include "i830_sdvo.h" #include "i2c_vid.h" @@ -209,6 +211,9 @@ typedef struct _I830SDVODriver { CARD32 output_device; /* SDVOB or SDVOC */ i830_sdvo_caps caps; + + CARD16 pixel_clock_min, pixel_clock_max; + int save_sdvo_mult; Bool save_sdvo_active_1, save_sdvo_active_2; i830_sdvo_dtd save_input_dtd_1, save_input_dtd_2; @@ -220,13 +225,62 @@ extern const char *i830_output_type_names[]; struct _I830OutputRec { int type; -/* int pipe; - int flags;*/ + int pipe; + Bool disabled; + + /** + * Turns the output on/off, or sets intermediate power levels if available. + * + * Unsupported intermediate modes drop to the lower power setting. If the + * mode is DPMSModeOff, the output must be disabled, as the DPLL may be + * disabled afterwards. + */ + void (*dpms)(ScrnInfoPtr pScrn, I830OutputPtr output, int mode); + + /** + * Saves the output's state for restoration on VT switch. + */ + void (*save)(ScrnInfoPtr pScrn, I830OutputPtr output); + + /** + * Restore's the output's state at VT switch. + */ + void (*restore)(ScrnInfoPtr pScrn, I830OutputPtr output); + + /** + * Callback for testing a video mode for a given output. + * + * This function should only check for cases where a mode can't be supported + * on the pipe specifically, and not represent generic CRTC limitations. + * + * \return MODE_OK if the mode is valid, or another MODE_* otherwise. + */ + int (*mode_valid)(ScrnInfoPtr pScrn, I830OutputPtr output, + DisplayModePtr pMode); + + /** + * Callback for setting up a video mode before any pipe/dpll changes. + * + * \param pMode the mode that will be set, or NULL if the mode to be set is + * unknown (such as the restore path of VT switching). + */ + void (*pre_set_mode)(ScrnInfoPtr pScrn, I830OutputPtr output, + DisplayModePtr pMode); + + /** + * Callback for setting up a video mode after the DPLL update but before + * the plane is enabled. + */ + void (*post_set_mode)(ScrnInfoPtr pScrn, I830OutputPtr output, + DisplayModePtr pMode); + xf86MonPtr MonInfo; I2CBusPtr pI2CBus; I2CBusPtr pDDCBus; struct _I830DVODriver *i2c_drv; I830SDVOPtr sdvo_drv; + /** Output-private structure. Should replace i2c_drv and sdvo_drv */ + void *dev_priv; }; typedef struct _I830Rec { @@ -613,9 +667,14 @@ extern Bool I830FixOffset(ScrnInfoPtr pScrn, I830MemRange *mem); extern Bool I830I2CInit(ScrnInfoPtr pScrn, I2CBusPtr *bus_ptr, int i2c_reg, char *name); +/* i830_crt.c */ +void i830_crt_init(ScrnInfoPtr pScrn); + /* i830_dvo.c */ -Bool I830I2CDetectDVOControllers(ScrnInfoPtr pScrn, I2CBusPtr pI2CBus, - struct _I830DVODriver **retdrv); +void i830_dvo_init(ScrnInfoPtr pScrn); + +/* i830_lvds.c */ +void i830_lvds_init(ScrnInfoPtr pScrn); /* i830_memory.c */ Bool I830BindAGPMemory(ScrnInfoPtr pScrn); @@ -636,6 +695,9 @@ Bool I830RandRSetConfig(ScreenPtr pScreen, Rotation rotation, int rate, Rotation I830GetRotation(ScreenPtr pScreen); void I830GetOriginalVirtualSize(ScrnInfoPtr pScrn, int *x, int *y); +/* i830_tv.c */ +void i830_tv_init(ScrnInfoPtr pScrn); + /* * 12288 is set as the maximum, chosen because it is enough for * 1920x1440@32bpp with a 2048 pixel line pitch with some to spare. diff --git a/src/i830_crt.c b/src/i830_crt.c new file mode 100644 index 00000000..7721a0c2 --- /dev/null +++ b/src/i830_crt.c @@ -0,0 +1,133 @@ +/* + * Copyright © 2006 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Eric Anholt <eric@anholt.net> + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "xf86.h" +#include "i830.h" + +static void +i830_crt_dpms(ScrnInfoPtr pScrn, I830OutputPtr output, int mode) +{ + I830Ptr pI830 = I830PTR(pScrn); + CARD32 temp; + + temp = INREG(ADPA); + temp &= ~(ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE); + temp &= ~ADPA_DAC_ENABLE; + + switch(mode) { + case DPMSModeOn: + temp |= ADPA_DAC_ENABLE; + break; + case DPMSModeStandby: + temp |= ADPA_DAC_ENABLE | ADPA_HSYNC_CNTL_DISABLE; + break; + case DPMSModeSuspend: + temp |= ADPA_DAC_ENABLE | ADPA_VSYNC_CNTL_DISABLE; + break; + case DPMSModeOff: + temp |= ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE; + break; + } + + OUTREG(ADPA, temp); +} + +static void +i830_crt_save(ScrnInfoPtr pScrn, I830OutputPtr output) +{ + I830Ptr pI830 = I830PTR(pScrn); + + pI830->saveADPA = INREG(ADPA); +} + +static void +i830_crt_restore(ScrnInfoPtr pScrn, I830OutputPtr output) +{ + I830Ptr pI830 = I830PTR(pScrn); + + OUTREG(ADPA, pI830->saveADPA); +} + +static int +i830_crt_mode_valid(ScrnInfoPtr pScrn, I830OutputPtr output, + DisplayModePtr pMode) +{ + return MODE_OK; +} + +static void +i830_crt_pre_set_mode(ScrnInfoPtr pScrn, I830OutputPtr output, + DisplayModePtr pMode) +{ +} + +static void +i830_crt_post_set_mode(ScrnInfoPtr pScrn, I830OutputPtr output, + DisplayModePtr pMode) +{ + I830Ptr pI830 = I830PTR(pScrn); + + CARD32 adpa; + + adpa = ADPA_DAC_ENABLE; + + if (pMode->Flags & V_PHSYNC) + adpa |= ADPA_HSYNC_ACTIVE_HIGH; + if (pMode->Flags & V_PVSYNC) + adpa |= ADPA_VSYNC_ACTIVE_HIGH; + + if (output->pipe == 0) + adpa |= ADPA_PIPE_A_SELECT; + else + adpa |= ADPA_PIPE_B_SELECT; + + OUTREG(ADPA, adpa); +} + +void +i830_crt_init(ScrnInfoPtr pScrn) +{ + I830Ptr pI830 = I830PTR(pScrn); + + pI830->output[pI830->num_outputs].type = I830_OUTPUT_ANALOG; + pI830->output[pI830->num_outputs].dpms = i830_crt_dpms; + pI830->output[pI830->num_outputs].save = i830_crt_save; + pI830->output[pI830->num_outputs].restore = i830_crt_restore; + 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; + + /* Set up the DDC bus. */ + I830I2CInit(pScrn, &pI830->output[pI830->num_outputs].pDDCBus, + GPIOA, "CRTDDC_A"); + + pI830->num_outputs++; +} diff --git a/src/i830_display.c b/src/i830_display.c index 2b53128c..9b20d65c 100644 --- a/src/i830_display.c +++ b/src/i830_display.c @@ -226,7 +226,7 @@ i830FindBestPLL(ScrnInfoPtr pScrn, int outputs, int target, int refclk, return (err != target); } -static void +void i830WaitForVblank(ScrnInfoPtr pScreen) { /* Wait for 20ms, i.e. one cycle at 50hz. */ @@ -260,129 +260,171 @@ i830PipeSetBase(ScrnInfoPtr pScrn, int pipe, int x, int y) } /** - * Sets the given video mode on the given pipe. Assumes that plane A feeds - * pipe A, and plane B feeds pipe B. Should not affect the other planes/pipes. + * In the current world order, there is a list of per-pipe modes, which may or + * may not include the mode that was asked to be set by XFree86's mode + * selection. Find the closest one, in the following preference order: + * + * - Equality + * - Closer in size to the requested mode, but no larger + * - Closer in refresh rate to the requested mode. */ -Bool -i830PipeSetMode(ScrnInfoPtr pScrn, DisplayModePtr pMode, int pipe) +static DisplayModePtr +i830PipeFindClosestMode(ScrnInfoPtr pScrn, int pipe, DisplayModePtr pMode) { I830Ptr pI830 = I830PTR(pScrn); - int m1 = 0, m2 = 0, n = 0, p1 = 0, p2 = 0; - CARD32 dpll = 0, fp = 0, temp; - CARD32 htot, hblank, hsync, vtot, vblank, vsync, dspcntr; - CARD32 pipesrc, dspsize, adpa; - CARD32 sdvob = 0, sdvoc = 0, dvo = 0; - Bool ok, is_sdvo, is_dvo; - int refclk, pixel_clock, sdvo_pixel_multiply; - int outputs; - DisplayModePtr pMasterMode = pMode; - - assert(pMode->VRefresh != 0.0); - /* If we've got a list of modes probed for the device, find the best match - * in there to the requested mode. + DisplayModePtr pBest = NULL, pScan; + + /* If the pipe doesn't have any detected modes, just let the system try to + * spam the desired mode in. */ - if (pI830->pipeMon[pipe] != NULL) { - DisplayModePtr pBest = NULL, pScan; + if (pI830->pipeMon[pipe] == NULL) { + xf86DrvMsg(pScrn->scrnIndex, X_WARNING, + "No pipe mode list for pipe %d," + "continuing with desired mode\n", pipe); + return pMode; + } + + assert(pScan->VRefresh != 0.0); + for (pScan = pI830->pipeMon[pipe]->Modes; pScan != NULL; + pScan = pScan->next) { + /* If there's an exact match, we're done. */ + if (I830ModesEqual(pScan, pMode)) { + pBest = pMode; + break; + } - assert(pScan->VRefresh != 0.0); - for (pScan = pI830->pipeMon[pipe]->Modes; pScan != NULL; - pScan = pScan->next) + /* Reject if it's larger than the desired mode. */ + if (pScan->HDisplay > pMode->HDisplay || + pScan->VDisplay > pMode->VDisplay) { - /* If there's an exact match, we're done. */ - if (I830ModesEqual(pScan, pMode)) { - pBest = pMode; - break; - } + continue; + } - /* Reject if it's larger than the desired mode. */ - if (pScan->HDisplay > pMode->HDisplay || - pScan->VDisplay > pMode->VDisplay) - { - continue; - } + if (pBest == NULL) { + pBest = pScan; + continue; + } - if (pBest == NULL) { - pBest = pScan; - continue; - } - /* Find if it's closer to the right size than the current best - * option. - */ - if ((pScan->HDisplay > pBest->HDisplay && - pScan->VDisplay >= pBest->VDisplay) || - (pScan->HDisplay >= pBest->HDisplay && - pScan->VDisplay > pBest->VDisplay)) - { - pBest = pScan; - continue; - } - /* Find if it's still closer to the right refresh than the current - * best resolution. - */ - if (pScan->HDisplay == pBest->HDisplay && - pScan->VDisplay == pBest->VDisplay && - (fabs(pScan->VRefresh - pMode->VRefresh) < - fabs(pBest->VRefresh - pMode->VRefresh))) - { - pBest = pScan; - } + /* Find if it's closer to the right size than the current best + * option. + */ + if ((pScan->HDisplay > pBest->HDisplay && + pScan->VDisplay >= pBest->VDisplay) || + (pScan->HDisplay >= pBest->HDisplay && + pScan->VDisplay > pBest->VDisplay)) + { + pBest = pScan; + continue; } - if (pBest != NULL) { - xf86DrvMsg(pScrn->scrnIndex, X_ERROR, - "Choosing pipe %d's mode %dx%d@%.1f instead of xf86 " - "mode %dx%d@%.1f\n", pipe, - pBest->HDisplay, pBest->VDisplay, pBest->VRefresh, - pMode->HDisplay, pMode->VDisplay, pMode->VRefresh); - pMode = pBest; + + /* Find if it's still closer to the right refresh than the current + * best resolution. + */ + if (pScan->HDisplay == pBest->HDisplay && + pScan->VDisplay == pBest->VDisplay && + (fabs(pScan->VRefresh - pMode->VRefresh) < + fabs(pBest->VRefresh - pMode->VRefresh))) { + pBest = pScan; } } + + if (pBest == NULL) { + xf86DrvMsg(pScrn->scrnIndex, X_WARNING, + "No suitable mode found to program for the pipe.\n" + " continuing with desired mode %dx%d@%.1f\n", + pMode->HDisplay, pMode->VDisplay, pMode->VRefresh); + } else if (!I830ModesEqual(pBest, pMode)) { + xf86DrvMsg(pScrn->scrnIndex, X_WARNING, + "Choosing pipe %d's mode %dx%d@%.1f instead of xf86 " + "mode %dx%d@%.1f\n", pipe, + pBest->HDisplay, pBest->VDisplay, pBest->VRefresh, + pMode->HDisplay, pMode->VDisplay, pMode->VRefresh); + pMode = pBest; + } + return pMode; +} + +/** + * Sets the given video mode on the given pipe. Assumes that plane A feeds + * pipe A, and plane B feeds pipe B. Should not affect the other planes/pipes. + */ +Bool +i830PipeSetMode(ScrnInfoPtr pScrn, DisplayModePtr pMode, int pipe) +{ + I830Ptr pI830 = I830PTR(pScrn); + int m1 = 0, m2 = 0, n = 0, p1 = 0, p2 = 0; + CARD32 dpll = 0, fp = 0, temp; + CARD32 htot, hblank, hsync, vtot, vblank, vsync, dspcntr; + CARD32 pipesrc, dspsize; + Bool ok, is_sdvo = FALSE, is_dvo = FALSE; + Bool is_crt = FALSE, is_lvds = FALSE, is_tv = FALSE; + int refclk, pixel_clock; + int outputs, i; + int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR; + int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF; + int fp_reg = (pipe == 0) ? FPA0 : FPB0; + int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B; + int htot_reg = (pipe == 0) ? HTOTAL_A : HTOTAL_B; + int hblank_reg = (pipe == 0) ? HBLANK_A : HBLANK_B; + int hsync_reg = (pipe == 0) ? HSYNC_A : HSYNC_B; + int vtot_reg = (pipe == 0) ? VTOTAL_A : VTOTAL_B; + int vblank_reg = (pipe == 0) ? VBLANK_A : VBLANK_B; + int vsync_reg = (pipe == 0) ? VSYNC_A : VSYNC_B; + int dspsize_reg = (pipe == 0) ? DSPASIZE : DSPBSIZE; + int dspstride_reg = (pipe == 0) ? DSPASTRIDE : DSPBSTRIDE; + int dsppos_reg = (pipe == 0) ? DSPAPOS : DSPBPOS; + int pipesrc_reg = (pipe == 0) ? PIPEASRC : PIPEBSRC; + if (pipe == 0) outputs = pI830->operatingDevices & 0xff; else outputs = (pI830->operatingDevices >> 8) & 0xff; - if (outputs & PIPE_LCD_ACTIVE) { - if (I830ModesEqual(&pI830->pipeCurMode[pipe], pMasterMode)) - return TRUE; - } else { - if (I830ModesEqual(&pI830->pipeCurMode[pipe], pMode)) - return TRUE; - } + if (I830ModesEqual(&pI830->pipeCurMode[pipe], pMode)) + return TRUE; xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Requested pix clock: %d\n", pMode->Clock); - if ((outputs & PIPE_LCD_ACTIVE) && (outputs & ~PIPE_LCD_ACTIVE)) { + for (i = 0; i < pI830->num_outputs; i++) { + if (pI830->output[i].pipe != pipe || pI830->output[i].disabled) + continue; + + switch (pI830->output[i].type) { + case I830_OUTPUT_LVDS: + is_lvds = TRUE; + break; + case I830_OUTPUT_SDVO: + is_sdvo = TRUE; + break; + case I830_OUTPUT_DVO: + is_dvo = TRUE; + break; + case I830_OUTPUT_TVOUT: + is_tv = TRUE; + break; + case I830_OUTPUT_ANALOG: + is_crt = TRUE; + break; + } + } + + if (is_lvds && (is_sdvo || is_dvo || is_tv || is_crt)) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Can't enable LVDS and non-LVDS on the same pipe\n"); return FALSE; } - if (((outputs & PIPE_TV_ACTIVE) && (outputs & ~PIPE_TV_ACTIVE)) || - ((outputs & PIPE_TV2_ACTIVE) && (outputs & ~PIPE_TV2_ACTIVE))) { + if (is_tv && (is_sdvo || is_dvo || is_crt || is_lvds)) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, - "Can't enable a TV and any other output on the same pipe\n"); + "Can't enable a TV and any other output on the same " + "pipe\n"); return FALSE; } - if (pipe == 0 && (outputs & PIPE_LCD_ACTIVE)) { + if (pipe == 0 && is_lvds) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Can't support LVDS on pipe A\n"); return FALSE; } - if ((outputs & PIPE_DFP_ACTIVE) || (outputs & PIPE_DFP2_ACTIVE)) { - /* We'll change how we control outputs soon, but to get the SDVO code up - * and running, just check for these two possibilities. - */ - if (IS_I9XX(pI830)) { - is_sdvo = TRUE; - is_dvo = FALSE; - } else { - is_dvo = TRUE; - is_sdvo = FALSE; - } - } else { - is_sdvo = FALSE; - is_dvo = FALSE; - } htot = (pMode->CrtcHDisplay - 1) | ((pMode->CrtcHTotal - 1) << 16); hblank = (pMode->CrtcHBlankStart - 1) | ((pMode->CrtcHBlankEnd - 1) << 16); @@ -393,8 +435,8 @@ i830PipeSetMode(ScrnInfoPtr pScrn, DisplayModePtr pMode, int pipe) pipesrc = ((pMode->HDisplay - 1) << 16) | (pMode->VDisplay - 1); dspsize = ((pMode->VDisplay - 1) << 16) | (pMode->HDisplay - 1); pixel_clock = pMode->Clock; - if (outputs & PIPE_LCD_ACTIVE && pI830->panel_fixed_hactive != 0) - { + + if (is_lvds && pI830->panel_fixed_hactive != 0) { /* To enable panel fitting, we need to set the pipe timings to that of * the screen at its full resolution. So, drop the timings from the * BIOS VBT tables here. @@ -420,30 +462,24 @@ i830PipeSetMode(ScrnInfoPtr pScrn, DisplayModePtr pMode, int pipe) pI830->panel_fixed_vsyncwidth - 1) << 16); pixel_clock = pI830->panel_fixed_clock; - if (pMasterMode->HDisplay <= pI830->panel_fixed_hactive && - pMasterMode->HDisplay <= pI830->panel_fixed_vactive) + if (pMode->HDisplay <= pI830->panel_fixed_hactive && + pMode->HDisplay <= pI830->panel_fixed_vactive) { - pipesrc = ((pMasterMode->HDisplay - 1) << 16) | - (pMasterMode->VDisplay - 1); - dspsize = ((pMasterMode->VDisplay - 1) << 16) | - (pMasterMode->HDisplay - 1); + pipesrc = ((pMode->HDisplay - 1) << 16) | + (pMode->VDisplay - 1); + dspsize = ((pMode->VDisplay - 1) << 16) | + (pMode->HDisplay - 1); } } - if (pMode->Clock >= 100000) - sdvo_pixel_multiply = 1; - else if (pMode->Clock >= 50000) - sdvo_pixel_multiply = 2; - else - sdvo_pixel_multiply = 4; - /* In SDVO, we need to keep the clock on the bus between 1Ghz and 2Ghz. * The clock on the bus is 10 times the pixel clock normally. If that * would be too low, we run the DPLL at a multiple of the pixel clock, and - * tell the SDVO device the multiplier so it can throw away the dummy bytes. + * tell the SDVO device the multiplier so it can throw away the dummy + * bytes. */ if (is_sdvo) { - pixel_clock *= sdvo_pixel_multiply; + pixel_clock *= i830_sdvo_get_pixel_multiplier(pMode); } if (IS_I9XX(pI830)) { @@ -461,7 +497,7 @@ i830PipeSetMode(ScrnInfoPtr pScrn, DisplayModePtr pMode, int pipe) dpll = DPLL_VCO_ENABLE | DPLL_VGA_MODE_DIS; if (IS_I9XX(pI830)) { - if (outputs & PIPE_LCD_ACTIVE) + if (is_lvds) dpll |= DPLLB_MODE_LVDS; else dpll |= DPLLB_MODE_DAC_SERIAL; @@ -486,55 +522,15 @@ i830PipeSetMode(ScrnInfoPtr pScrn, DisplayModePtr pMode, int pipe) dpll |= PLL_P2_DIVIDE_BY_4; } - if (outputs & (PIPE_TV_ACTIVE | PIPE_TV2_ACTIVE)) + if (is_tv) dpll |= PLL_REF_INPUT_TVCLKINBC; #if 0 - else if (outputs & (PIPE_LCD_ACTIVE)) + else if (is_lvds) dpll |= PLLB_REF_INPUT_SPREADSPECTRUMIN; #endif else dpll |= PLL_REF_INPUT_DREFCLK; - if (is_dvo) { - dpll |= DPLL_DVO_HIGH_SPEED; - - /* Save the data order, since I don't know what it should be set to. */ - dvo = INREG(DVOC) & (DVO_PRESERVE_MASK | DVO_DATA_ORDER_GBRG); - dvo |= DVO_ENABLE; - dvo |= DVO_DATA_ORDER_FP | DVO_BORDER_ENABLE | DVO_BLANK_ACTIVE_HIGH; - - if (pipe == 1) - dvo |= DVO_PIPE_B_SELECT; - - if (pMode->Flags & V_PHSYNC) - dvo |= DVO_HSYNC_ACTIVE_HIGH; - if (pMode->Flags & V_PVSYNC) - dvo |= DVO_VSYNC_ACTIVE_HIGH; - - OUTREG(DVOC, dvo & ~DVO_ENABLE); - } - - if (is_sdvo) { - dpll |= DPLL_DVO_HIGH_SPEED; - - ErrorF("DVOB: %08x\nDVOC: %08x\n", (int)INREG(SDVOB), (int)INREG(SDVOC)); - - sdvob = INREG(SDVOB) & SDVOB_PRESERVE_MASK; - sdvoc = INREG(SDVOC) & SDVOC_PRESERVE_MASK; - sdvob |= SDVO_ENABLE | (9 << 19) | SDVO_BORDER_ENABLE; - sdvoc |= 9 << 19; - if (pipe == 1) - sdvob |= SDVO_PIPE_B_SELECT; - - if (IS_I945G(pI830) || IS_I945GM(pI830)) - dpll |= (sdvo_pixel_multiply - 1) << SDVO_MULTIPLIER_SHIFT_HIRES; - else - sdvob |= (sdvo_pixel_multiply - 1) << SDVO_PORT_MULTIPLY_SHIFT; - - OUTREG(SDVOC, INREG(SDVOC) & ~SDVO_ENABLE); - OUTREG(SDVOB, INREG(SDVOB) & ~SDVO_ENABLE); - } - fp = ((n - 2) << 16) | ((m1 - 2) << 8) | (m2 - 2); #if 1 @@ -576,150 +572,53 @@ i830PipeSetMode(ScrnInfoPtr pScrn, DisplayModePtr pMode, int pipe) dspcntr |= DISPPLANE_GAMMA_ENABLE; } - if (is_sdvo) - adpa = ADPA_DAC_DISABLE; - else - adpa = ADPA_DAC_ENABLE; - if (pMode->Flags & V_PHSYNC) - adpa |= ADPA_HSYNC_ACTIVE_HIGH; - if (pMode->Flags & V_PVSYNC) - adpa |= ADPA_VSYNC_ACTIVE_HIGH; - - if (pipe == 0) { + if (pipe == 0) dspcntr |= DISPPLANE_SEL_PIPE_A; - adpa |= ADPA_PIPE_A_SELECT; - } else { + else dspcntr |= DISPPLANE_SEL_PIPE_B; - adpa |= ADPA_PIPE_B_SELECT; - } OUTREG(VGACNTRL, VGA_DISP_DISABLE); - /* Set up display timings and PLLs for the pipe. */ - if (pipe == 0) { - /* First, disable display planes */ - temp = INREG(DSPACNTR); - OUTREG(DSPACNTR, temp & ~DISPLAY_PLANE_ENABLE); - - /* Wait for vblank for the disable to take effect */ - i830WaitForVblank(pScrn); - - /* Next, disable display pipes */ - temp = INREG(PIPEACONF); - OUTREG(PIPEACONF, temp & ~PIPEACONF_ENABLE); - - OUTREG(FPA0, fp); - OUTREG(DPLL_A, dpll); - - OUTREG(HTOTAL_A, htot); - OUTREG(HBLANK_A, hblank); - OUTREG(HSYNC_A, hsync); - OUTREG(VTOTAL_A, vtot); - OUTREG(VBLANK_A, vblank); - OUTREG(VSYNC_A, vsync); - OUTREG(DSPASTRIDE, pScrn->displayWidth * pI830->cpp); - OUTREG(DSPASIZE, dspsize); - OUTREG(DSPAPOS, 0); - i830PipeSetBase(pScrn, pipe, pI830->pipeX[pipe], pI830->pipeX[pipe]); - OUTREG(PIPEASRC, pipesrc); - - /* Then, turn the pipe on first */ - temp = INREG(PIPEACONF); - OUTREG(PIPEACONF, temp | PIPEACONF_ENABLE); - - /* And then turn the plane on */ - OUTREG(DSPACNTR, dspcntr); - } else { - /* Always make sure the LVDS is off before we play with DPLLs and pipe - * configuration. - */ - i830SetLVDSPanelPower(pScrn, FALSE); - - /* First, disable display planes */ - temp = INREG(DSPBCNTR); - OUTREG(DSPBCNTR, temp & ~DISPLAY_PLANE_ENABLE); - - /* Wait for vblank for the disable to take effect */ - i830WaitForVblank(pScrn); + /* Finally, set the mode. */ + /* First, disable display planes */ + temp = INREG(dspcntr_reg); + OUTREG(dspcntr_reg, temp & ~DISPLAY_PLANE_ENABLE); - /* Next, disable display pipes */ - temp = INREG(PIPEBCONF); - OUTREG(PIPEBCONF, temp & ~PIPEBCONF_ENABLE); + /* Wait for vblank for the disable to take effect */ + i830WaitForVblank(pScrn); - if (outputs & PIPE_LCD_ACTIVE) { - /* Disable the PLL before messing with LVDS enable */ - OUTREG(FPB0, fp & ~DPLL_VCO_ENABLE); - - /* LVDS must be powered on before PLL is enabled and before power - * sequencing the panel. - */ - temp = INREG(LVDS); - OUTREG(LVDS, temp | LVDS_PORT_EN | LVDS_PIPEB_SELECT); - } - - OUTREG(FPB0, fp); - OUTREG(DPLL_B, dpll); - OUTREG(HTOTAL_B, htot); - OUTREG(HBLANK_B, hblank); - OUTREG(HSYNC_B, hsync); - OUTREG(VTOTAL_B, vtot); - OUTREG(VBLANK_B, vblank); - OUTREG(VSYNC_B, vsync); - OUTREG(DSPBSTRIDE, pScrn->displayWidth * pI830->cpp); - OUTREG(DSPBSIZE, dspsize); - OUTREG(DSPBPOS, 0); - i830PipeSetBase(pScrn, pipe, pI830->pipeX[pipe], pI830->pipeY[pipe]); - OUTREG(PIPEBSRC, pipesrc); - - if (outputs & PIPE_LCD_ACTIVE) { - CARD32 pfit_control; - - /* Enable automatic panel scaling so that non-native modes fill the - * screen. - */ - /* XXX: Allow (auto-?) enabling of 8-to-6 dithering */ - pfit_control = (PFIT_ENABLE | - VERT_AUTO_SCALE | HORIZ_AUTO_SCALE | - VERT_INTERP_BILINEAR | HORIZ_INTERP_BILINEAR); - if (pI830->panel_wants_dither) - pfit_control |= PANEL_8TO6_DITHER_ENABLE; - OUTREG(PFIT_CONTROL, pfit_control); - } - - /* Then, turn the pipe on first */ - temp = INREG(PIPEBCONF); - OUTREG(PIPEBCONF, temp | PIPEBCONF_ENABLE); + /* Next, disable display pipes */ + temp = INREG(pipeconf_reg); + OUTREG(pipeconf_reg, temp & ~PIPEACONF_ENABLE); - /* And then turn the plane on */ - OUTREG(DSPBCNTR, dspcntr); + OUTREG(fp_reg, fp); + OUTREG(dpll_reg, dpll); - if (outputs & PIPE_LCD_ACTIVE) { - i830SetLVDSPanelPower(pScrn, TRUE); - } + for (i = 0; i < pI830->num_outputs; i++) { + if (pI830->output[i].pipe == pipe) + pI830->output[i].post_set_mode(pScrn, &pI830->output[i], pMode); } - if (outputs & PIPE_CRT_ACTIVE) - OUTREG(ADPA, adpa); + OUTREG(htot_reg, htot); + OUTREG(hblank_reg, hblank); + OUTREG(hsync_reg, hsync); + OUTREG(vtot_reg, vtot); + OUTREG(vblank_reg, vblank); + OUTREG(vsync_reg, vsync); + OUTREG(dspstride_reg, pScrn->displayWidth * pI830->cpp); + OUTREG(dspsize_reg, dspsize); + OUTREG(dsppos_reg, 0); + i830PipeSetBase(pScrn, pipe, pI830->pipeX[pipe], pI830->pipeX[pipe]); + OUTREG(pipesrc_reg, pipesrc); - if (is_dvo) { - /*OUTREG(DVOB_SRCDIM, (pMode->HDisplay << DVO_SRCDIM_HORIZONTAL_SHIFT) | - (pMode->VDisplay << DVO_SRCDIM_VERTICAL_SHIFT));*/ - OUTREG(DVOC_SRCDIM, (pMode->HDisplay << DVO_SRCDIM_HORIZONTAL_SHIFT) | - (pMode->VDisplay << DVO_SRCDIM_VERTICAL_SHIFT)); - /*OUTREG(DVOB, dvo);*/ - OUTREG(DVOC, dvo); - } + /* Then, turn the pipe on first */ + temp = INREG(pipeconf_reg); + OUTREG(pipeconf_reg, temp | PIPEACONF_ENABLE); - if (is_sdvo) { - OUTREG(SDVOB, sdvob); - OUTREG(SDVOC, sdvoc); - } + /* And then turn the plane on */ + OUTREG(dspcntr_reg, dspcntr); - if (outputs & PIPE_LCD_ACTIVE) { - pI830->pipeCurMode[pipe] = *pMasterMode; - } else { - pI830->pipeCurMode[pipe] = *pMode; - } + pI830->pipeCurMode[pipe] = *pMode; return TRUE; } @@ -728,48 +627,13 @@ void i830DisableUnusedFunctions(ScrnInfoPtr pScrn) { I830Ptr pI830 = I830PTR(pScrn); - int outputsA, outputsB; - - outputsA = pI830->operatingDevices & 0xff; - outputsB = (pI830->operatingDevices >> 8) & 0xff; + int i; xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Disabling unused functions\n"); - /* First, disable the unused outputs */ - if ((outputsA & PIPE_CRT_ACTIVE) == 0 && - (outputsB & PIPE_CRT_ACTIVE) == 0) - { - CARD32 adpa = INREG(ADPA); - - if (adpa & ADPA_DAC_ENABLE) { - xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Disabling CRT output\n"); - OUTREG(ADPA, adpa & ~ADPA_DAC_ENABLE); - } - } - - if ((outputsB & PIPE_LCD_ACTIVE) == 0) { - CARD32 pp_status = INREG(PP_STATUS); - - if (pp_status & PP_ON) { - xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Disabling LVDS output\n"); - i830SetLVDSPanelPower(pScrn, FALSE); - } - } - - if (IS_I9XX(pI830) && ((outputsA & PIPE_DFP_ACTIVE) == 0 && - (outputsB & PIPE_DFP_ACTIVE) == 0)) - { - CARD32 sdvob = INREG(SDVOB); - CARD32 sdvoc = INREG(SDVOC); - - if (sdvob & SDVO_ENABLE) { - xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Disabling SDVOB output\n"); - OUTREG(SDVOB, sdvob & ~SDVO_ENABLE); - } - if (sdvoc & SDVO_ENABLE) { - xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Disabling SDVOC output\n"); - OUTREG(SDVOC, sdvoc & ~SDVO_ENABLE); - } + for (i = 0; i < pI830->num_outputs; i++) { + if (pI830->output[i].disabled) + pI830->output[i].dpms(pScrn, &pI830->output[i], DPMSModeOff); } /* Now, any unused plane, pipe, and DPLL (FIXME: except for DVO, i915 @@ -839,7 +703,6 @@ i830SetMode(ScrnInfoPtr pScrn, DisplayModePtr pMode) { I830Ptr pI830 = I830PTR(pScrn); Bool ok = TRUE; - CARD32 planeA, planeB; #ifdef XF86DRI Bool didLock = FALSE; #endif @@ -864,30 +727,21 @@ i830SetMode(ScrnInfoPtr pScrn, DisplayModePtr pMode) } for (i = 0; i < pI830->num_outputs; i++) { - struct _I830OutputRec *output = &pI830->output[i]; - - if (output->sdvo_drv) - I830SDVOPreSetMode(output->sdvo_drv, pMode); - - if (output->i2c_drv != NULL) - output->i2c_drv->vid_rec->Mode(output->i2c_drv->dev_priv, - pMode); + pI830->output[i].pre_set_mode(pScrn, &pI830->output[i], pMode); } if (pI830->planeEnabled[0]) { - ok = i830PipeSetMode(pScrn, pMode, 0); + ok = i830PipeSetMode(pScrn, i830PipeFindClosestMode(pScrn, 0, pMode), + 0); if (!ok) goto done; } if (pI830->planeEnabled[1]) { - ok = i830PipeSetMode(pScrn, pMode, 1); + ok = i830PipeSetMode(pScrn, i830PipeFindClosestMode(pScrn, 1, pMode), + 1); if (!ok) goto done; } - for (i = 0; i < pI830->num_outputs; i++) { - if (pI830->output[i].sdvo_drv) - I830SDVOPostSetMode(pI830->output[i].sdvo_drv, pMode); - } xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Mode bandwidth is %d Mpixel/s\n", (int)(pMode->HDisplay * pMode->VDisplay * @@ -919,18 +773,7 @@ i830SetMode(ScrnInfoPtr pScrn, DisplayModePtr pMode) i830DisableUnusedFunctions(pScrn); - planeA = INREG(DSPACNTR); - planeB = INREG(DSPBCNTR); - - xf86DrvMsg(pScrn->scrnIndex, X_INFO, - "Display plane A is now %s and connected to %s.\n", - pI830->planeEnabled[0] ? "enabled" : "disabled", - planeA & DISPPLANE_SEL_PIPE_MASK ? "Pipe B" : "Pipe A"); - if (pI830->availablePipes == 2) - xf86DrvMsg(pScrn->scrnIndex, X_INFO, - "Display plane B is now %s and connected to %s.\n", - pI830->planeEnabled[1] ? "enabled" : "disabled", - planeB & DISPPLANE_SEL_PIPE_MASK ? "Pipe B" : "Pipe A"); + i830DescribeOutputConfiguration(pScrn); #ifdef XF86DRI I830DRISetVBlankInterrupt (pScrn, TRUE); @@ -946,6 +789,52 @@ done: return ok; } +void +i830DescribeOutputConfiguration(ScrnInfoPtr pScrn) +{ + I830Ptr pI830 = I830PTR(pScrn); + int i; + + xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Output configuration:\n"); + + for (i = 0; i < pI830->availablePipes; i++) { + CARD32 dspcntr = INREG(DSPACNTR + (DSPBCNTR - DSPACNTR) * i); + + xf86DrvMsg(pScrn->scrnIndex, X_INFO, + " Display plane %c is now %s and connected to pipe %c.\n", + 'A' + i, + pI830->planeEnabled[i] ? "enabled" : "disabled", + dspcntr & DISPPLANE_SEL_PIPE_MASK ? 'B' : 'A'); + } + + for (i = 0; i < pI830->num_outputs; i++) { + const char *name = NULL; + + switch (pI830->output[i].type) { + case I830_OUTPUT_ANALOG: + name = "CRT"; + break; + case I830_OUTPUT_LVDS: + name = "LVDS"; + break; + case I830_OUTPUT_SDVO: + name = "SDVO"; + break; + case I830_OUTPUT_DVO: + name = "DVO"; + break; + case I830_OUTPUT_TVOUT: + name = "TV"; + break; + } + + xf86DrvMsg(pScrn->scrnIndex, X_INFO, + " Output %s is %sabled and connected to pipe %c\n", + name, pI830->output[i].disabled ? "dis" : "en", + pI830->output[i].pipe == 0 ? 'A' : 'B'); + } +} + /** * Uses CRT_HOTPLUG_EN and CRT_HOTPLUG_STAT to detect CRT presence. * @@ -1092,42 +981,3 @@ i830DetectCRT(ScrnInfoPtr pScrn, Bool allow_disturb) return FALSE; } - -/** - * Sets the power state for the panel. - */ -void -i830SetLVDSPanelPower(ScrnInfoPtr pScrn, Bool on) -{ - I830Ptr pI830 = I830PTR(pScrn); - CARD32 pp_status, pp_control; - CARD32 blc_pwm_ctl; - int backlight_duty_cycle; - - blc_pwm_ctl = INREG (BLC_PWM_CTL); - backlight_duty_cycle = blc_pwm_ctl & BACKLIGHT_DUTY_CYCLE_MASK; - if (backlight_duty_cycle) - pI830->backlight_duty_cycle = backlight_duty_cycle; - - if (on) { - OUTREG(PP_STATUS, INREG(PP_STATUS) | PP_ON); - OUTREG(PP_CONTROL, INREG(PP_CONTROL) | POWER_TARGET_ON); - do { - pp_status = INREG(PP_STATUS); - pp_control = INREG(PP_CONTROL); - } while (!(pp_status & PP_ON) && !(pp_control & POWER_TARGET_ON)); - OUTREG(BLC_PWM_CTL, - (blc_pwm_ctl & ~BACKLIGHT_DUTY_CYCLE_MASK) | - pI830->backlight_duty_cycle); - } else { - OUTREG(BLC_PWM_CTL, - (blc_pwm_ctl & ~BACKLIGHT_DUTY_CYCLE_MASK)); - - OUTREG(PP_STATUS, INREG(PP_STATUS) & ~PP_ON); - OUTREG(PP_CONTROL, INREG(PP_CONTROL) & ~POWER_TARGET_ON); - do { - pp_status = INREG(PP_STATUS); - pp_control = INREG(PP_CONTROL); - } while ((pp_status & PP_ON) || (pp_control & POWER_TARGET_ON)); - } -} diff --git a/src/i830_display.h b/src/i830_display.h index 2b808ad8..97194062 100644 --- a/src/i830_display.h +++ b/src/i830_display.h @@ -32,9 +32,9 @@ Bool i830SetMode(ScrnInfoPtr pScrn, DisplayModePtr pMode); Bool i830DetectCRT(ScrnInfoPtr pScrn, Bool allow_disturb); void i830SetLVDSPanelPower(ScrnInfoPtr pScrn, Bool on); void i830PipeSetBase(ScrnInfoPtr pScrn, int pipe, int x, int y); +void i830WaitForVblank(ScrnInfoPtr pScrn); +void i830DescribeOutputConfiguration(ScrnInfoPtr pScrn); /* i830_sdvo.c */ -I830SDVOPtr I830SDVOInit(ScrnInfoPtr pScrn, int output_index, - CARD32 output_device); Bool I830SDVOPreSetMode(I830SDVOPtr s, DisplayModePtr mode); Bool I830SDVOPostSetMode(I830SDVOPtr s, DisplayModePtr mode); diff --git a/src/i830_driver.c b/src/i830_driver.c index 07a9d59b..972df137 100644 --- a/src/i830_driver.c +++ b/src/i830_driver.c @@ -900,56 +900,30 @@ I830UseDDC(ScrnInfoPtr pScrn) } #endif +/** + * Set up the outputs according to what type of chip we are. + * + * Some outputs may not initialize, due to allocation failure or because a + * controller chip isn't found. + */ static void -I830SetupOutputBusses(ScrnInfoPtr pScrn) +I830SetupOutputs(ScrnInfoPtr pScrn) { I830Ptr pI830 = I830PTR(pScrn); - int i = 0; - Bool ret; /* everyone has at least a single analog output */ - pI830->output[i].type = I830_OUTPUT_ANALOG; - - /* setup the DDC bus for the analog output */ - I830I2CInit(pScrn, &pI830->output[i].pDDCBus, GPIOA, "CRTDDC_A"); - i++; + i830_crt_init(pScrn); - if (IS_MOBILE(pI830) && !IS_I830(pI830)) { - /* Set up integrated LVDS */ - pI830->output[i].type = I830_OUTPUT_LVDS; - I830I2CInit(pScrn, &pI830->output[i].pDDCBus, GPIOC, "LVDSDDC_C"); - i++; - } + /* Set up integrated LVDS */ + if (IS_MOBILE(pI830) && !IS_I830(pI830)) + i830_lvds_init(pScrn); if (IS_I9XX(pI830)) { - /* Set up SDVOB */ - pI830->output[i].type = I830_OUTPUT_SDVO; - I830I2CInit(pScrn, &pI830->output[i].pI2CBus, GPIOE, "SDVOCTRL_E"); - I830SDVOInit(pScrn, i, SDVOB); - i++; - - /* Set up SDVOC */ - pI830->output[i].type = I830_OUTPUT_SDVO; - pI830->output[i].pI2CBus = pI830->output[i-1].pI2CBus; - I830SDVOInit(pScrn, i, SDVOC); - i++; + i830_sdvo_init(pScrn, SDVOB); + i830_sdvo_init(pScrn, SDVOC); } else { - /* set up DVO */ - pI830->output[i].type = I830_OUTPUT_DVO; - I830I2CInit(pScrn, &pI830->output[i].pDDCBus, GPIOD, "DVODDC_D"); - I830I2CInit(pScrn, &pI830->output[i].pI2CBus, GPIOE, "DVOI2C_E"); - - ret = I830I2CDetectDVOControllers(pScrn, pI830->output[i].pI2CBus, - &pI830->output[i].i2c_drv); - if (ret == TRUE) { - xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Found i2c %s on %08lX\n", - pI830->output[i].i2c_drv->modulename, - pI830->output[i].pI2CBus->DriverPrivate.uval); - } - - i++; + i830_dvo_init(pScrn); } - pI830->num_outputs = i; } static void @@ -970,7 +944,7 @@ I830PreInitDDC(ScrnInfoPtr pScrn) if (xf86LoadSubModule(pScrn, "i2c")) { xf86LoaderReqSymLists(I810i2cSymbols, NULL); - I830SetupOutputBusses(pScrn); + I830SetupOutputs(pScrn); pI830->ddc2 = TRUE; } else { @@ -1011,14 +985,12 @@ I830DetectMonitors(ScrnInfoPtr pScrn) xf86PrintEDID(pI830->output[i].MonInfo); break; case I830_OUTPUT_SDVO: - if (pI830->output[i].sdvo_drv != NULL) { - pI830->output[i].MonInfo = xf86DoEDID_DDC2(pScrn->scrnIndex, - pI830->output[i].pDDCBus); + pI830->output[i].MonInfo = xf86DoEDID_DDC2(pScrn->scrnIndex, + pI830->output[i].pDDCBus); - xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "DDC SDVO %d, %08lX\n", i, - pI830->output[i].pDDCBus->DriverPrivate.uval); - xf86PrintEDID(pI830->output[i].MonInfo); - } + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "DDC SDVO %d, %08lX\n", i, + pI830->output[i].pDDCBus->DriverPrivate.uval); + xf86PrintEDID(pI830->output[i].MonInfo); break; case I830_OUTPUT_UNUSED: break; @@ -1556,7 +1528,7 @@ I830PreInit(ScrnInfoPtr pScrn, int flags) * that we might find early in the list. This hackery will go away when we * start doing independent per-head mode selection. */ - for (i = MAX_OUTPUTS - 1; i >= 0; i--) { + for (i = pI830->num_outputs - 1; i >= 0; i--) { if (pI830->output[i].MonInfo) { pScrn->monitor->DDC = pI830->output[i].MonInfo; xf86SetDDCproperties(pScrn, pI830->output[i].MonInfo); @@ -1577,8 +1549,7 @@ I830PreInit(ScrnInfoPtr pScrn, int flags) /* Blacklist machines with known broken BIOSes */ if (pI830->PciInfo->chipType == PCI_CHIP_I945_GM) { - if ((pI830->PciInfo->subsysVendor == 0xa0a0) && - (pI830->PciInfo->subsysCard == 0x0589)) /* aopen mini pc */ + if (pI830->PciInfo->subsysVendor == 0xa0a0) /* aopen mini pc */ has_lvds = FALSE; if ((pI830->PciInfo->subsysVendor == 0x8086) && @@ -1700,9 +1671,8 @@ I830PreInit(ScrnInfoPtr pScrn, int flags) * now. Though really, it's just a name at the moment, since we don't * treat different SDVO outputs differently. */ - for (i = 0; i < MAX_OUTPUTS; i++) { - if (pI830->output[i].type == I830_OUTPUT_SDVO && - pI830->output[i].sdvo_drv != NULL) { + for (i = 0; i < pI830->num_outputs; i++) { + if (pI830->output[i].type == I830_OUTPUT_SDVO) { if (!I830DetectSDVODisplays(pScrn, i)) continue; @@ -1756,6 +1726,48 @@ I830PreInit(ScrnInfoPtr pScrn, int flags) pI830->Clone = TRUE; } + + /* Perform the pipe assignment of outputs. This code shouldn't exist, + * but for now we're supporting the existing MonitorLayout configuration + * scheme. + */ + for (i = 0; i < pI830->num_outputs; i++) { + pI830->output[i].disabled = FALSE; + + switch (pI830->output[i].type) { + case I830_OUTPUT_LVDS: + if (pI830->MonType1 & PIPE_LFP) + pI830->output[i].pipe = 0; + else if (pI830->MonType2 & PIPE_LFP) + pI830->output[i].pipe = 1; + else + pI830->output[i].disabled = TRUE; + break; + case I830_OUTPUT_ANALOG: + if (pI830->MonType1 & PIPE_CRT) + pI830->output[i].pipe = 0; + else if (pI830->MonType2 & PIPE_CRT) + pI830->output[i].pipe = 1; + else + pI830->output[i].disabled = TRUE; + break; + case I830_OUTPUT_DVO: + case I830_OUTPUT_SDVO: + if (pI830->MonType1 & PIPE_DFP) + pI830->output[i].pipe = 0; + else if (pI830->MonType2 & PIPE_DFP) + pI830->output[i].pipe = 1; + else + pI830->output[i].disabled = TRUE; + break; + default: + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Unhandled output type\n"); + break; + } + } + + + pI830->CloneRefresh = 60; /* default to 60Hz */ if (xf86GetOptValInteger(pI830->Options, OPTION_CLONE_REFRESH, &(pI830->CloneRefresh))) { @@ -2593,31 +2605,6 @@ SaveHWState(ScrnInfoPtr pScrn) pI830->saveVCLK_POST_DIV = INREG(VCLK_POST_DIV); pI830->saveVGACNTRL = INREG(VGACNTRL); - pI830->saveADPA = INREG(ADPA); - - pI830->savePFIT_CONTROL = INREG(PFIT_CONTROL); - pI830->savePP_ON = INREG(LVDSPP_ON); - pI830->savePP_OFF = INREG(LVDSPP_OFF); - pI830->saveLVDS = INREG(LVDS); - pI830->savePP_CONTROL = INREG(PP_CONTROL); - pI830->savePP_CYCLE = INREG(PP_CYCLE); - pI830->saveBLC_PWM_CTL = INREG(BLC_PWM_CTL); - pI830->backlight_duty_cycle = (pI830->saveBLC_PWM_CTL & - BACKLIGHT_DUTY_CYCLE_MASK); - /* - * If the light is off at server startup, just make it full brightness - */ - if (!pI830->backlight_duty_cycle) - pI830->backlight_duty_cycle = ((pI830->saveBLC_PWM_CTL & - BACKLIGHT_MODULATION_FREQ_MASK) >> - BACKLIGHT_MODULATION_FREQ_SHIFT); - - if (!IS_I9XX(pI830)) { - pI830->saveDVOA = INREG(DVOA); - pI830->saveDVOB = INREG(DVOB); - pI830->saveDVOC = INREG(DVOC); - } - for(i = 0; i < 7; i++) { pI830->saveSWF[i] = INREG(SWF0 + (i << 2)); pI830->saveSWF[i+7] = INREG(SWF00 + (i << 2)); @@ -2627,17 +2614,8 @@ SaveHWState(ScrnInfoPtr pScrn) pI830->saveSWF[16] = INREG(SWF32); for (i = 0; i < pI830->num_outputs; i++) { - if (pI830->output[i].type == I830_OUTPUT_DVO && - pI830->output[i].i2c_drv != NULL) - { - pI830->output[i].i2c_drv->vid_rec->SaveRegs( - pI830->output[i].i2c_drv->dev_priv); - } - if (pI830->output[i].type == I830_OUTPUT_SDVO && - pI830->output[i].sdvo_drv != NULL) - { - i830SDVOSave(pScrn, i); - } + if (pI830->output[i].save != NULL) + pI830->output[i].save(pScrn, &pI830->output[i]); } vgaHWUnlock(hwp); @@ -2675,19 +2653,13 @@ RestoreHWState(ScrnInfoPtr pScrn) temp = INREG(PIPEBCONF); OUTREG(PIPEBCONF, temp & ~PIPEBCONF_ENABLE); - /* XXX: Wait for a vblank */ - sleep(1); - - i830SetLVDSPanelPower(pScrn, FALSE); - + /* Disable outputs if necessary */ for (i = 0; i < pI830->num_outputs; i++) { - if (pI830->output[i].type == I830_OUTPUT_SDVO && - pI830->output[i].sdvo_drv != NULL) - { - i830SDVOPreRestore(pScrn, i); - } + pI830->output[i].pre_set_mode(pScrn, &pI830->output[i], NULL); } + i830WaitForVblank(pScrn); + OUTREG(FPA0, pI830->saveFPA0); OUTREG(FPA1, pI830->saveFPA1); OUTREG(DPLL_A, pI830->saveDPLL_A); @@ -2726,17 +2698,15 @@ RestoreHWState(ScrnInfoPtr pScrn) } } + for (i = 0; i < pI830->num_outputs; i++) { + pI830->output[i].restore(pScrn, &pI830->output[i]); + } + if (IS_I965G(pI830)) { OUTREG(DSPASURF, pI830->saveDSPABASE); OUTREG(DSPBSURF, pI830->saveDSPBBASE); } - OUTREG(BLC_PWM_CTL, pI830->saveBLC_PWM_CTL); - OUTREG(LVDSPP_ON, pI830->savePP_ON); - OUTREG(LVDSPP_OFF, pI830->savePP_OFF); - OUTREG(PP_CYCLE, pI830->savePP_CYCLE); - OUTREG(PFIT_CONTROL, pI830->savePFIT_CONTROL); - OUTREG(VCLK_DIVISOR_VGA0, pI830->saveVCLK_DIVISOR_VGA0); OUTREG(VCLK_DIVISOR_VGA1, pI830->saveVCLK_DIVISOR_VGA1); OUTREG(VCLK_POST_DIV, pI830->saveVCLK_POST_DIV); @@ -2748,30 +2718,6 @@ RestoreHWState(ScrnInfoPtr pScrn) OUTREG(DSPACNTR, pI830->saveDSPACNTR); OUTREG(DSPBCNTR, pI830->saveDSPBCNTR); - OUTREG(ADPA, pI830->saveADPA); - OUTREG(LVDS, pI830->saveLVDS); - if (!IS_I9XX(pI830)) { - OUTREG(DVOA, pI830->saveDVOA); - OUTREG(DVOB, pI830->saveDVOB); - OUTREG(DVOC, pI830->saveDVOC); - } - - for (i = 0; i < pI830->num_outputs; i++) { - if (pI830->output[i].type == I830_OUTPUT_DVO && - pI830->output[i].i2c_drv != NULL) - { - pI830->output[i].i2c_drv->vid_rec->RestoreRegs( - pI830->output[i].i2c_drv->dev_priv); - } - if (pI830->output[i].type == I830_OUTPUT_SDVO && - pI830->output[i].sdvo_drv != NULL) - { - i830SDVOPostRestore(pScrn, i); - } - } - - OUTREG(PP_CONTROL, pI830->savePP_CONTROL); - for(i = 0; i < 7; i++) { OUTREG(SWF0 + (i << 2), pI830->saveSWF[i]); OUTREG(SWF00 + (i << 2), pI830->saveSWF[i+7]); @@ -4197,39 +4143,6 @@ I830SaveScreen(ScreenPtr pScreen, int mode) return TRUE; } -static void -I830DPMSCRT(ScrnInfoPtr pScrn, int mode) -{ - I830Ptr pI830 = I830PTR(pScrn); - CARD32 temp; - - temp = INREG(ADPA); - temp &= ~(ADPA_HSYNC_CNTL_DISABLE|ADPA_VSYNC_CNTL_DISABLE); - switch(mode) { - case DPMSModeOn: - break; - case DPMSModeStandby: - temp |= ADPA_HSYNC_CNTL_DISABLE; - break; - case DPMSModeSuspend: - temp |= ADPA_VSYNC_CNTL_DISABLE; - break; - case DPMSModeOff: - temp |= ADPA_HSYNC_CNTL_DISABLE|ADPA_VSYNC_CNTL_DISABLE; - break; - } - OUTREG(ADPA, temp); -} - -static void -I830DPMSLVDS(ScrnInfoPtr pScrn, int mode) -{ - if (mode == DPMSModeOn) - i830SetLVDSPanelPower(pScrn, TRUE); - else - i830SetLVDSPanelPower(pScrn, FALSE); -} - /* Use the VBE version when available. */ static void I830DisplayPowerManagementSet(ScrnInfoPtr pScrn, int PowerManagementMode, @@ -4239,6 +4152,10 @@ I830DisplayPowerManagementSet(ScrnInfoPtr pScrn, int PowerManagementMode, int i; CARD32 temp, ctrl, base; + for (i = 0; i < pI830->num_outputs; i++) { + pI830->output[i].dpms(pScrn, &pI830->output[i], PowerManagementMode); + } + for (i = 0; i < pI830->availablePipes; i++) { if (i == 0) { ctrl = DSPACNTR; @@ -4260,22 +4177,6 @@ I830DisplayPowerManagementSet(ScrnInfoPtr pScrn, int PowerManagementMode, } } - if (pI830->operatingDevices & (PIPE_CRT_ACTIVE | (PIPE_CRT_ACTIVE<<8))) { - I830DPMSCRT(pScrn, PowerManagementMode); - } - - if (pI830->operatingDevices & (PIPE_LCD_ACTIVE | (PIPE_LCD_ACTIVE<<8))) { - I830DPMSLVDS(pScrn, PowerManagementMode); - } - - if (pI830->operatingDevices & (PIPE_DFP_ACTIVE | (PIPE_DFP_ACTIVE<<8))) { - /* TBD */ - } - - if (pI830->operatingDevices & (PIPE_DFP2_ACTIVE | (PIPE_DFP2_ACTIVE<<8))) { - /* TBD */ - } - if (pI830->CursorInfoRec && !pI830->SWCursor && pI830->cursorOn) { if (PowerManagementMode == DPMSModeOn) pI830->CursorInfoRec->ShowCursor(pScrn); @@ -4466,14 +4367,11 @@ i830MonitorDetectDebugger(ScrnInfoPtr pScrn) xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Detected CRT as %s in %dms\n", found_crt ? "connected" : "disconnected", finish - start); - for (i = 0; i < MAX_OUTPUTS; i++) { + for (i = 0; i < pI830->num_outputs; i++) { Bool found_sdvo = TRUE; - if (pI830->output[i].type != I830_OUTPUT_SDVO || - pI830->output[i].sdvo_drv == NULL) - { + if (pI830->output[i].type != I830_OUTPUT_SDVO) continue; - } start = GetTimeInMillis(); found_sdvo = I830DetectSDVODisplays(pScrn, i); finish = GetTimeInMillis(); diff --git a/src/i830_dvo.c b/src/i830_dvo.c index 242e3dd0..01858f2c 100644 --- a/src/i830_dvo.c +++ b/src/i830_dvo.c @@ -22,7 +22,8 @@ DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -**************************************************************************/ +****** +********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" @@ -30,6 +31,7 @@ USE OR OTHER DEALINGS IN THE SOFTWARE. #include "xf86.h" #include "i830.h" +#include "i810_reg.h" #include "sil164/sil164.h" #include "ch7xxx/ch7xxx.h" @@ -54,7 +56,95 @@ struct _I830DVODriver i830_dvo_drivers[] = #define I830_NUM_DVO_DRIVERS (sizeof(i830_dvo_drivers)/sizeof(struct _I830DVODriver)) -Bool +static void +i830_dvo_dpms(ScrnInfoPtr pScrn, I830OutputPtr output, int mode) +{ + if (mode == DPMSModeOn) + output->i2c_drv->vid_rec->Power(output->i2c_drv->dev_priv, TRUE); + else + output->i2c_drv->vid_rec->Power(output->i2c_drv->dev_priv, FALSE); +} + +static void +i830_dvo_save(ScrnInfoPtr pScrn, I830OutputPtr output) +{ + I830Ptr pI830 = I830PTR(pScrn); + + /* Each output should probably just save the registers it touches, but for + * now, use more overkill. + */ + pI830->saveDVOA = INREG(DVOA); + pI830->saveDVOB = INREG(DVOB); + pI830->saveDVOC = INREG(DVOC); + + output->i2c_drv->vid_rec->SaveRegs(output->i2c_drv->dev_priv); +} + +static void +i830_dvo_restore(ScrnInfoPtr pScrn, I830OutputPtr output) +{ + I830Ptr pI830 = I830PTR(pScrn); + + OUTREG(DVOA, pI830->saveDVOA); + OUTREG(DVOB, pI830->saveDVOB); + OUTREG(DVOC, pI830->saveDVOC); + + output->i2c_drv->vid_rec->RestoreRegs(output->i2c_drv->dev_priv); +} + +static int +i830_dvo_mode_valid(ScrnInfoPtr pScrn, I830OutputPtr output, + DisplayModePtr pMode) +{ + if (output->i2c_drv->vid_rec->ModeValid(output->i2c_drv->dev_priv, pMode)) + return MODE_OK; + else + return MODE_BAD; +} + +static void +i830_dvo_pre_set_mode(ScrnInfoPtr pScrn, I830OutputPtr output, + DisplayModePtr pMode) +{ + I830Ptr pI830 = I830PTR(pScrn); + + output->i2c_drv->vid_rec->Mode(output->i2c_drv->dev_priv, pMode); + + OUTREG(DVOC, INREG(DVOC) & ~DVO_ENABLE); +} + +static void +i830_dvo_post_set_mode(ScrnInfoPtr pScrn, I830OutputPtr output, + DisplayModePtr pMode) +{ + I830Ptr pI830 = I830PTR(pScrn); + CARD32 dvo; + int dpll_reg = (output->pipe == 0) ? DPLL_A : DPLL_B; + + /* Save the data order, since I don't know what it should be set to. */ + dvo = INREG(DVOC) & (DVO_PRESERVE_MASK | DVO_DATA_ORDER_GBRG); + dvo |= DVO_ENABLE; + dvo |= DVO_DATA_ORDER_FP | DVO_BORDER_ENABLE | DVO_BLANK_ACTIVE_HIGH; + + if (output->pipe == 1) + dvo |= DVO_PIPE_B_SELECT; + + if (pMode->Flags & V_PHSYNC) + dvo |= DVO_HSYNC_ACTIVE_HIGH; + if (pMode->Flags & V_PVSYNC) + dvo |= DVO_VSYNC_ACTIVE_HIGH; + + OUTREG(dpll_reg, INREG(dpll_reg) | DPLL_DVO_HIGH_SPEED); + + /*OUTREG(DVOB_SRCDIM, (pMode->HDisplay << DVO_SRCDIM_HORIZONTAL_SHIFT) | + (pMode->VDisplay << DVO_SRCDIM_VERTICAL_SHIFT));*/ + OUTREG(DVOC_SRCDIM, (pMode->HDisplay << DVO_SRCDIM_HORIZONTAL_SHIFT) | + (pMode->VDisplay << DVO_SRCDIM_VERTICAL_SHIFT)); + /*OUTREG(DVOB, dvo);*/ + OUTREG(DVOC, dvo); +} + +static Bool I830I2CDetectDVOControllers(ScrnInfoPtr pScrn, I2CBusPtr pI2CBus, struct _I830DVODriver **retdrv) { @@ -84,3 +174,45 @@ I830I2CDetectDVOControllers(ScrnInfoPtr pScrn, I2CBusPtr pI2CBus, } return FALSE; } + +void +i830_dvo_init(ScrnInfoPtr pScrn) +{ + I830Ptr pI830 = I830PTR(pScrn); + Bool ret; + int i = pI830->num_outputs; + + pI830->output[i].type = I830_OUTPUT_DVO; + pI830->output[i].dpms = i830_dvo_dpms; + pI830->output[i].save = i830_dvo_save; + pI830->output[i].restore = i830_dvo_restore; + 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; + + /* Set up the I2C and DDC buses */ + ret = I830I2CInit(pScrn, &pI830->output[i].pI2CBus, GPIOE, "DVOI2C_E"); + if (!ret) + return; + + ret = I830I2CInit(pScrn, &pI830->output[i].pDDCBus, GPIOD, "DVODDC_D"); + if (!ret) { + xf86DestroyI2CBusRec(pI830->output[i].pI2CBus, TRUE, TRUE); + return; + } + + /* Now, try to find a controller */ + ret = I830I2CDetectDVOControllers(pScrn, pI830->output[i].pI2CBus, + &pI830->output[i].i2c_drv); + if (ret) { + xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Found i2c %s on %08lX\n", + pI830->output[i].i2c_drv->modulename, + pI830->output[i].pI2CBus->DriverPrivate.uval); + } else { + xf86DestroyI2CBusRec(pI830->output[i].pI2CBus, TRUE, TRUE); + xf86DestroyI2CBusRec(pI830->output[i].pDDCBus, TRUE, TRUE); + return; + } + + pI830->num_outputs++; +} diff --git a/src/i830_lvds.c b/src/i830_lvds.c new file mode 100644 index 00000000..399324f0 --- /dev/null +++ b/src/i830_lvds.c @@ -0,0 +1,199 @@ +/* + * Copyright © 2006 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Eric Anholt <eric@anholt.net> + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "xf86.h" +#include "i830.h" + +/** + * Sets the power state for the panel. + */ +static void +i830SetLVDSPanelPower(ScrnInfoPtr pScrn, Bool on) +{ + I830Ptr pI830 = I830PTR(pScrn); + CARD32 pp_status, pp_control; + CARD32 blc_pwm_ctl; + int backlight_duty_cycle; + + blc_pwm_ctl = INREG (BLC_PWM_CTL); + backlight_duty_cycle = blc_pwm_ctl & BACKLIGHT_DUTY_CYCLE_MASK; + if (backlight_duty_cycle) + pI830->backlight_duty_cycle = backlight_duty_cycle; + + if (on) { + OUTREG(PP_STATUS, INREG(PP_STATUS) | PP_ON); + OUTREG(PP_CONTROL, INREG(PP_CONTROL) | POWER_TARGET_ON); + do { + pp_status = INREG(PP_STATUS); + pp_control = INREG(PP_CONTROL); + } while (!(pp_status & PP_ON) && !(pp_control & POWER_TARGET_ON)); + OUTREG(BLC_PWM_CTL, + (blc_pwm_ctl & ~BACKLIGHT_DUTY_CYCLE_MASK) | + pI830->backlight_duty_cycle); + } else { + OUTREG(BLC_PWM_CTL, + (blc_pwm_ctl & ~BACKLIGHT_DUTY_CYCLE_MASK)); + + OUTREG(PP_STATUS, INREG(PP_STATUS) & ~PP_ON); + OUTREG(PP_CONTROL, INREG(PP_CONTROL) & ~POWER_TARGET_ON); + do { + pp_status = INREG(PP_STATUS); + pp_control = INREG(PP_CONTROL); + } while ((pp_status & PP_ON) || (pp_control & POWER_TARGET_ON)); + } +} + +static void +i830_lvds_dpms(ScrnInfoPtr pScrn, I830OutputPtr output, int mode) +{ + if (mode == DPMSModeOn) + i830SetLVDSPanelPower(pScrn, TRUE); + else + i830SetLVDSPanelPower(pScrn, FALSE); +} + +static void +i830_lvds_save(ScrnInfoPtr pScrn, I830OutputPtr output) +{ + I830Ptr pI830 = I830PTR(pScrn); + + pI830->savePFIT_CONTROL = INREG(PFIT_CONTROL); + pI830->savePP_ON = INREG(LVDSPP_ON); + pI830->savePP_OFF = INREG(LVDSPP_OFF); + pI830->saveLVDS = INREG(LVDS); + pI830->savePP_CONTROL = INREG(PP_CONTROL); + pI830->savePP_CYCLE = INREG(PP_CYCLE); + pI830->saveBLC_PWM_CTL = INREG(BLC_PWM_CTL); + pI830->backlight_duty_cycle = (pI830->saveBLC_PWM_CTL & + BACKLIGHT_DUTY_CYCLE_MASK); + + /* + * If the light is off at server startup, just make it full brightness + */ + if (pI830->backlight_duty_cycle == 0) { + pI830->backlight_duty_cycle = + (pI830->saveBLC_PWM_CTL & BACKLIGHT_MODULATION_FREQ_MASK) >> + BACKLIGHT_MODULATION_FREQ_SHIFT; + } +} + +static void +i830_lvds_restore(ScrnInfoPtr pScrn, I830OutputPtr output) +{ + I830Ptr pI830 = I830PTR(pScrn); + + OUTREG(BLC_PWM_CTL, pI830->saveBLC_PWM_CTL); + OUTREG(LVDSPP_ON, pI830->savePP_ON); + OUTREG(LVDSPP_OFF, pI830->savePP_OFF); + OUTREG(PP_CYCLE, pI830->savePP_CYCLE); + OUTREG(PFIT_CONTROL, pI830->savePFIT_CONTROL); + OUTREG(LVDS, pI830->saveLVDS); + OUTREG(PP_CONTROL, pI830->savePP_CONTROL); + if (pI830->savePP_CONTROL & POWER_TARGET_ON) + i830SetLVDSPanelPower(pScrn, TRUE); + else + i830SetLVDSPanelPower(pScrn, FALSE); +} + +static int +i830_lvds_mode_valid(ScrnInfoPtr pScrn, I830OutputPtr output, + DisplayModePtr pMode) +{ + return MODE_OK; +} + +static void +i830_lvds_pre_set_mode(ScrnInfoPtr pScrn, I830OutputPtr output, + DisplayModePtr pMode) +{ + /* Always make sure the LVDS is off before we play with DPLLs and pipe + * configuration. We can skip this in some cases (for example, going + * between hi-res modes with automatic panel scaling are fine), but be + * conservative for now. + */ + i830SetLVDSPanelPower(pScrn, FALSE); +} + +static void +i830_lvds_post_set_mode(ScrnInfoPtr pScrn, I830OutputPtr output, + DisplayModePtr pMode) +{ + I830Ptr pI830 = I830PTR(pScrn); + CARD32 pfit_control; + + /* Enable automatic panel scaling so that non-native modes fill the + * screen. Should be enabled before the pipe is enabled, according to + * register description. + */ + pfit_control = (PFIT_ENABLE | + VERT_AUTO_SCALE | HORIZ_AUTO_SCALE | + VERT_INTERP_BILINEAR | HORIZ_INTERP_BILINEAR); + + if (pI830->panel_wants_dither) + pfit_control |= PANEL_8TO6_DITHER_ENABLE; + + OUTREG(PFIT_CONTROL, pfit_control); + + /* Disable the PLL before messing with LVDS enable */ + OUTREG(FPB0, INREG(FPB0) & ~DPLL_VCO_ENABLE); + + /* LVDS must be powered on before PLL is enabled and before power + * sequencing the panel. + */ + OUTREG(LVDS, INREG(LVDS) | LVDS_PORT_EN | LVDS_PIPEB_SELECT); + + /* Re-enable the PLL */ + OUTREG(FPB0, INREG(FPB0) | DPLL_VCO_ENABLE); + + i830SetLVDSPanelPower(pScrn, TRUE); +} + +void +i830_lvds_init(ScrnInfoPtr pScrn) +{ + I830Ptr pI830 = I830PTR(pScrn); + + pI830->output[pI830->num_outputs].type = I830_OUTPUT_LVDS; + pI830->output[pI830->num_outputs].dpms = i830_lvds_dpms; + pI830->output[pI830->num_outputs].save = i830_lvds_save; + pI830->output[pI830->num_outputs].restore = i830_lvds_restore; + 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; + + /* Set up the LVDS DDC channel. Most panels won't support it, but it can + * be useful if available. + */ + I830I2CInit(pScrn, &pI830->output[pI830->num_outputs].pDDCBus, + GPIOC, "LVDSDDC_C"); + + pI830->num_outputs++; +} diff --git a/src/i830_modes.c b/src/i830_modes.c index 9301dda4..bc4536d5 100644 --- a/src/i830_modes.c +++ b/src/i830_modes.c @@ -766,7 +766,7 @@ I830ReprobePipeModeList(ScrnInfoPtr pScrn, int pipe) else outputs = (pI830->operatingDevices >> 8) & 0xff; - for (i = 0; i < MAX_OUTPUTS; i++) { + for (i = 0; i < pI830->num_outputs; i++) { switch (pI830->output[i].type) { case I830_OUTPUT_ANALOG: if (outputs & PIPE_CRT) { @@ -779,14 +779,12 @@ I830ReprobePipeModeList(ScrnInfoPtr pScrn, int pipe) } break; case I830_OUTPUT_DVO: - if (outputs & PIPE_DFP && pI830->output[i].i2c_drv != NULL) { + if (outputs & PIPE_DFP) { output_index = i; } break; case I830_OUTPUT_SDVO: - if (outputs & PIPE_DFP && - pI830->output[i].sdvo_drv != NULL) - { + if (outputs & PIPE_DFP) { output_index = i; } break; diff --git a/src/i830_sdvo.c b/src/i830_sdvo.c index c7625419..76080dfa 100644 --- a/src/i830_sdvo.c +++ b/src/i830_sdvo.c @@ -34,6 +34,7 @@ USE OR OTHER DEALINGS IN THE SOFTWARE. #include "compiler.h" #include "i830.h" #include "i830_display.h" +#include "i810_reg.h" #include "i830_sdvo_regs.h" CARD16 curr_table[6]; @@ -169,6 +170,17 @@ I830SDVOReadInputRegs(I830SDVOPtr s) ErrorF("\n"); } +int +i830_sdvo_get_pixel_multiplier(DisplayModePtr pMode) +{ + if (pMode->Clock >= 100000) + return 1; + else if (pMode->Clock >= 50000) + return 2; + else + return 4; +} + /* Sets the control bus switch to either point at one of the DDC buses or the * PROM. It resets from the DDC bus back to internal registers at the next I2C * STOP. PROM access is terminated by accessing an internal register. @@ -520,9 +532,11 @@ I830SDVOSetClockRateMult(I830SDVOPtr s, CARD8 val) return TRUE; } -Bool -I830SDVOPreSetMode(I830SDVOPtr s, DisplayModePtr mode) +static void +i830_sdvo_pre_set_mode(ScrnInfoPtr pScrn, I830OutputPtr output, + DisplayModePtr mode) { + I830Ptr pI830 = I830PTR(pScrn); CARD16 clock = mode->Clock/10, width = mode->CrtcHDisplay; CARD16 height = mode->CrtcVDisplay; CARD16 h_blank_len, h_sync_len, v_blank_len, v_sync_len; @@ -531,8 +545,8 @@ I830SDVOPreSetMode(I830SDVOPtr s, DisplayModePtr mode) CARD8 c16a[8]; CARD8 c17a[8]; CARD16 out_timings[6]; - CARD16 clock_min, clock_max; Bool out1, out2; + I830SDVOPtr s = output->sdvo_drv; /* do some mode translations */ h_blank_len = mode->CrtcHBlankEnd - mode->CrtcHBlankStart; @@ -573,8 +587,6 @@ I830SDVOPreSetMode(I830SDVOPtr s, DisplayModePtr mode) out_timings[5] = c17a[3] | ((short)c17a[2] << 8); I830SDVOSetTargetInput(s, FALSE, FALSE); - I830SDVOGetInputPixelClockRange(s, &clock_min, &clock_max); - ErrorF("clock min/max: %d %d\n", clock_min, clock_max); I830SDVOGetActiveOutputs(s, &out1, &out2); @@ -600,22 +612,34 @@ I830SDVOPreSetMode(I830SDVOPtr s, DisplayModePtr mode) out_timings[5]); I830SDVOSetTargetInput (s, FALSE, FALSE); - - if (clock >= 10000) + + switch (i830_sdvo_get_pixel_multiplier(mode)) { + case 1: I830SDVOSetClockRateMult(s, SDVO_CLOCK_RATE_MULT_1X); - else if (clock >= 5000) + break; + case 2: I830SDVOSetClockRateMult(s, SDVO_CLOCK_RATE_MULT_2X); - else + break; + case 4: I830SDVOSetClockRateMult(s, SDVO_CLOCK_RATE_MULT_4X); + break; + } - return TRUE; + OUTREG(SDVOC, INREG(SDVOC) & ~SDVO_ENABLE); + OUTREG(SDVOB, INREG(SDVOB) & ~SDVO_ENABLE); } -Bool -I830SDVOPostSetMode(I830SDVOPtr s, DisplayModePtr mode) +static void +i830_sdvo_post_set_mode(ScrnInfoPtr pScrn, I830OutputPtr output, + DisplayModePtr mode) { + I830Ptr pI830 = I830PTR(pScrn); Bool ret = TRUE; Bool out1, out2; + CARD32 dpll, sdvob, sdvoc; + int dpll_reg = (output->pipe == 0) ? DPLL_A : DPLL_B; + int sdvo_pixel_multiply; + I830SDVOPtr s = output->sdvo_drv; /* the BIOS writes out 6 commands post mode set */ /* two 03s, 04 05, 10, 1d */ @@ -636,14 +660,48 @@ I830SDVOPostSetMode(I830SDVOPtr s, DisplayModePtr mode) I830SDVOSetActiveOutputs(s, TRUE, FALSE); I830SDVOSetTargetInput (s, FALSE, FALSE); - return ret; + /* Set the SDVO control regs. */ + sdvob = INREG(SDVOB) & SDVOB_PRESERVE_MASK; + sdvoc = INREG(SDVOC) & SDVOC_PRESERVE_MASK; + sdvob |= SDVO_ENABLE | (9 << 19) | SDVO_BORDER_ENABLE; + sdvoc |= 9 << 19; + if (output->pipe == 1) + sdvob |= SDVO_PIPE_B_SELECT; + + dpll = INREG(dpll_reg); + + sdvo_pixel_multiply = i830_sdvo_get_pixel_multiplier(mode); + if (IS_I945G(pI830) || IS_I945GM(pI830)) + dpll |= (sdvo_pixel_multiply - 1) << SDVO_MULTIPLIER_SHIFT_HIRES; + else + sdvob |= (sdvo_pixel_multiply - 1) << SDVO_PORT_MULTIPLY_SHIFT; + + OUTREG(dpll_reg, dpll | DPLL_DVO_HIGH_SPEED); + + OUTREG(SDVOB, sdvob); + OUTREG(SDVOC, sdvoc); } -void -i830SDVOSave(ScrnInfoPtr pScrn, int output_index) +static void +i830_sdvo_dpms(ScrnInfoPtr pScrn, I830OutputPtr output, int mode) { I830Ptr pI830 = I830PTR(pScrn); - I830SDVOPtr sdvo = pI830->output[output_index].sdvo_drv; + I830SDVOPtr sdvo = output->sdvo_drv; + + if (mode != DPMSModeOn) { + I830SDVOSetActiveOutputs(sdvo, FALSE, FALSE); + OUTREG(SDVOB, INREG(SDVOB) & ~SDVO_ENABLE); + } else { + I830SDVOSetActiveOutputs(sdvo, TRUE, FALSE); + OUTREG(SDVOB, INREG(SDVOB) | SDVO_ENABLE); + } +} + +static void +i830_sdvo_save(ScrnInfoPtr pScrn, I830OutputPtr output) +{ + I830Ptr pI830 = I830PTR(pScrn); + I830SDVOPtr sdvo = output->sdvo_drv; sdvo->save_sdvo_mult = I830SDVOGetClockRateMult(sdvo); I830SDVOGetActiveOutputs(sdvo, &sdvo->save_sdvo_active_1, @@ -676,20 +734,11 @@ i830SDVOSave(ScrnInfoPtr pScrn, int output_index) sdvo->save_SDVOX = INREG(sdvo->output_device); } -void -i830SDVOPreRestore(ScrnInfoPtr pScrn, int output_index) -{ - I830Ptr pI830 = I830PTR(pScrn); - I830SDVOPtr sdvo = pI830->output[output_index].sdvo_drv; - - I830SDVOSetActiveOutputs(sdvo, FALSE, FALSE); -} - -void -i830SDVOPostRestore(ScrnInfoPtr pScrn, int output_index) +static void +i830_sdvo_restore(ScrnInfoPtr pScrn, I830OutputPtr output) { I830Ptr pI830 = I830PTR(pScrn); - I830SDVOPtr sdvo = pI830->output[output_index].sdvo_drv; + I830SDVOPtr sdvo = output->sdvo_drv; if (sdvo->caps.caps & 0x1) { I830SDVOSetTargetInput(sdvo, FALSE, FALSE); @@ -723,6 +772,21 @@ i830SDVOPostRestore(ScrnInfoPtr pScrn, int output_index) sdvo->save_sdvo_active_2); } +static int +i830_sdvo_mode_valid(ScrnInfoPtr pScrn, I830OutputPtr output, + DisplayModePtr pMode) +{ + I830SDVOPtr sdvo = output->sdvo_drv; + + if (sdvo->pixel_clock_min > pMode->Clock) + return MODE_CLOCK_HIGH; + + if (sdvo->pixel_clock_max < pMode->Clock) + return MODE_CLOCK_LOW; + + return MODE_OK; +} + static void I830SDVOGetCapabilities(I830SDVOPtr s, i830_sdvo_caps *caps) { @@ -815,95 +879,6 @@ I830SDVODDCI2CAddress(I2CDevPtr d, I2CSlaveAddr addr) return FALSE; } -I830SDVOPtr -I830SDVOInit(ScrnInfoPtr pScrn, int output_index, CARD32 output_device) -{ - I830Ptr pI830 = I830PTR(pScrn); - I830SDVOPtr sdvo; - int i; - unsigned char ch[0x40]; - I2CBusPtr i2cbus, ddcbus; - - i2cbus = pI830->output[output_index].pI2CBus; - - sdvo = xcalloc(1, sizeof(I830SDVORec)); - if (sdvo == NULL) - return NULL; - - if (output_device == SDVOB) { - sdvo->d.DevName = "SDVO Controller B"; - sdvo->d.SlaveAddr = 0x70; - } else { - sdvo->d.DevName = "SDVO Controller C"; - sdvo->d.SlaveAddr = 0x72; - } - sdvo->d.pI2CBus = i2cbus; - sdvo->d.DriverPrivate.ptr = sdvo; - sdvo->output_device = output_device; - - if (!xf86I2CDevInit(&sdvo->d)) { - xf86DrvMsg(pScrn->scrnIndex, X_ERROR, - "Failed to initialize SDVO I2C device %s\n", - output_device == SDVOB ? "SDVOB" : "SDVOC"); - xfree(sdvo); - return NULL; - } - - /* Set up our wrapper I2C bus for DDC. It acts just like the regular I2C - * bus, except that it does the control bus switch to DDC mode before every - * Start. While we only need to do it at Start after every Stop after a - * Start, extra attempts should be harmless. - */ - ddcbus = xf86CreateI2CBusRec(); - if (ddcbus == NULL) { - xf86DestroyI2CDevRec(&sdvo->d, 0); - xfree(sdvo); - return NULL; - } - if (output_device == SDVOB) - ddcbus->BusName = "SDVOB DDC Bus"; - else - ddcbus->BusName = "SDVOC DDC Bus"; - ddcbus->scrnIndex = i2cbus->scrnIndex; - ddcbus->I2CGetByte = I830SDVODDCI2CGetByte; - ddcbus->I2CPutByte = I830SDVODDCI2CPutByte; - ddcbus->I2CStart = I830SDVODDCI2CStart; - ddcbus->I2CStop = I830SDVODDCI2CStop; - ddcbus->I2CAddress = I830SDVODDCI2CAddress; - ddcbus->DriverPrivate.ptr = sdvo; - if (!xf86I2CBusInit(ddcbus)) { - xf86DestroyI2CDevRec(&sdvo->d, 0); - xfree(sdvo); - return NULL; - } - - pI830->output[output_index].pDDCBus = ddcbus; - - /* Read the regs to test if we can talk to the device */ - for (i = 0; i < 0x40; i++) { - if (!sReadByte(sdvo, i, &ch[i])) { - xf86DestroyI2CBusRec(pI830->output[output_index].pDDCBus, FALSE, - FALSE); - xf86DestroyI2CDevRec(&sdvo->d, 0); - xfree(sdvo); - return NULL; - } - } - - pI830->output[output_index].sdvo_drv = sdvo; - - I830SDVOGetCapabilities(sdvo, &sdvo->caps); - - xf86DrvMsg(pScrn->scrnIndex, X_INFO, - "SDVO device VID/DID: %02X:%02X.%02X, %02X, output 1: %c, output 2: %c\n", - sdvo->caps.vendor_id, sdvo->caps.device_id, - sdvo->caps.device_rev_id, sdvo->caps.caps, - sdvo->caps.output_0_supported ? 'Y' : 'N', - sdvo->caps.output_1_supported ? 'Y' : 'N'); - - return sdvo; -} - static void I830DumpSDVOCmd (I830SDVOPtr s, int opcode) { @@ -944,13 +919,11 @@ void I830DumpSDVO (ScrnInfoPtr pScrn) { I830Ptr pI830 = I830PTR(pScrn); - I830SDVOPtr s; int i; - for (i = 0; i < 4; i++) { - s = pI830->output[i].sdvo_drv; - if (s) - I830DumpOneSDVO (s); + for (i = 0; i < pI830->num_outputs; i++) { + if (pI830->output[i].type == I830_OUTPUT_SDVO) + I830DumpOneSDVO (pI830->output[i].sdvo_drv); } } @@ -979,3 +952,119 @@ I830DetectSDVODisplays(ScrnInfoPtr pScrn, int output_index) return (s->sdvo_regs[SDVO_I2C_RETURN_0] != 0 || s->sdvo_regs[SDVO_I2C_RETURN_1] != 0); } + +void +i830_sdvo_init(ScrnInfoPtr pScrn, int output_device) +{ + I830Ptr pI830 = I830PTR(pScrn); + I830SDVOPtr sdvo; + int i; + unsigned char ch[0x40]; + I2CBusPtr i2cbus = NULL, ddcbus; + + pI830->output[pI830->num_outputs].type = I830_OUTPUT_SDVO; + pI830->output[pI830->num_outputs].dpms = i830_sdvo_dpms; + pI830->output[pI830->num_outputs].save = i830_sdvo_save; + pI830->output[pI830->num_outputs].restore = i830_sdvo_restore; + pI830->output[pI830->num_outputs].mode_valid = i830_sdvo_mode_valid; + pI830->output[pI830->num_outputs].pre_set_mode = i830_sdvo_pre_set_mode; + pI830->output[pI830->num_outputs].post_set_mode = i830_sdvo_post_set_mode; + + /* Find an existing SDVO I2CBus from another output, or allocate it. */ + for (i = 0; i < pI830->num_outputs; i++) { + if (pI830->output[i].type == I830_OUTPUT_SDVO) + i2cbus = pI830->output[i].pI2CBus; + } + if (i2cbus == NULL) + I830I2CInit(pScrn, &i2cbus, GPIOE, "SDVOCTRL_E"); + if (i2cbus == NULL) + return; + + /* Allocate the SDVO output private data */ + sdvo = xcalloc(1, sizeof(I830SDVORec)); + if (sdvo == NULL) { + xf86DestroyI2CBusRec(i2cbus, TRUE, TRUE); + return; + } + + if (output_device == SDVOB) { + sdvo->d.DevName = "SDVO Controller B"; + sdvo->d.SlaveAddr = 0x70; + } else { + sdvo->d.DevName = "SDVO Controller C"; + sdvo->d.SlaveAddr = 0x72; + } + sdvo->d.pI2CBus = i2cbus; + sdvo->d.DriverPrivate.ptr = sdvo; + sdvo->output_device = output_device; + + if (!xf86I2CDevInit(&sdvo->d)) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "Failed to initialize SDVO I2C device %s\n", + output_device == SDVOB ? "SDVOB" : "SDVOC"); + xf86DestroyI2CBusRec(i2cbus, TRUE, TRUE); + xfree(sdvo); + return; + } + + /* Set up our wrapper I2C bus for DDC. It acts just like the regular I2C + * bus, except that it does the control bus switch to DDC mode before every + * Start. While we only need to do it at Start after every Stop after a + * Start, extra attempts should be harmless. + */ + ddcbus = xf86CreateI2CBusRec(); + if (ddcbus == NULL) { + xf86DestroyI2CDevRec(&sdvo->d, FALSE); + xf86DestroyI2CBusRec(i2cbus, TRUE, TRUE); + xfree(sdvo); + return; + } + if (output_device == SDVOB) + ddcbus->BusName = "SDVOB DDC Bus"; + else + ddcbus->BusName = "SDVOC DDC Bus"; + ddcbus->scrnIndex = i2cbus->scrnIndex; + ddcbus->I2CGetByte = I830SDVODDCI2CGetByte; + ddcbus->I2CPutByte = I830SDVODDCI2CPutByte; + ddcbus->I2CStart = I830SDVODDCI2CStart; + ddcbus->I2CStop = I830SDVODDCI2CStop; + ddcbus->I2CAddress = I830SDVODDCI2CAddress; + ddcbus->DriverPrivate.ptr = sdvo; + if (!xf86I2CBusInit(ddcbus)) { + xf86DestroyI2CDevRec(&sdvo->d, FALSE); + xf86DestroyI2CBusRec(i2cbus, TRUE, TRUE); + xfree(sdvo); + return; + } + + pI830->output[pI830->num_outputs].pI2CBus = i2cbus; + pI830->output[pI830->num_outputs].pDDCBus = ddcbus; + pI830->output[pI830->num_outputs].sdvo_drv = sdvo; + + /* Read the regs to test if we can talk to the device */ + for (i = 0; i < 0x40; i++) { + if (!sReadByte(sdvo, i, &ch[i])) { + xf86DestroyI2CBusRec(pI830->output[pI830->num_outputs].pDDCBus, + FALSE, FALSE); + xf86DestroyI2CDevRec(&sdvo->d, FALSE); + xf86DestroyI2CBusRec(i2cbus, TRUE, TRUE); + xfree(sdvo); + return; + } + } + + I830SDVOGetCapabilities(sdvo, &sdvo->caps); + + I830SDVOGetInputPixelClockRange(sdvo, &sdvo->pixel_clock_min, + &sdvo->pixel_clock_max); + + xf86DrvMsg(pScrn->scrnIndex, X_INFO, + "SDVO device VID/DID: %02X:%02X.%02X, %02X," + "output 1: %c, output 2: %c\n", + sdvo->caps.vendor_id, sdvo->caps.device_id, + sdvo->caps.device_rev_id, sdvo->caps.caps, + sdvo->caps.output_0_supported ? 'Y' : 'N', + sdvo->caps.output_1_supported ? 'Y' : 'N'); + + pI830->num_outputs++; +} diff --git a/src/i830_sdvo.h b/src/i830_sdvo.h index 52621e03..44bbfe47 100644 --- a/src/i830_sdvo.h +++ b/src/i830_sdvo.h @@ -56,13 +56,10 @@ typedef struct _i830_sdvo_dtd { } __attribute__((packed)) i830_sdvo_dtd; void -i830SDVOSave(ScrnInfoPtr pScrn, int output_index); +i830_sdvo_init(ScrnInfoPtr pScrn, int output_device); -void -i830SDVOPreRestore(ScrnInfoPtr pScrn, int output_index); - -void -i830SDVOPostRestore(ScrnInfoPtr pScrn, int output_index); +int +i830_sdvo_get_pixel_multiplier(DisplayModePtr pMode); Bool I830DetectSDVODisplays(ScrnInfoPtr pScrn, int output_index); diff --git a/src/i830_tv.c b/src/i830_tv.c new file mode 100644 index 00000000..2adbe91b --- /dev/null +++ b/src/i830_tv.c @@ -0,0 +1,430 @@ +/* + * Copyright © 2006 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Eric Anholt <eric@anholt.net> + * + */ + +/** @file + * Integrated TV-out support for the 915GM and 945GM. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "xf86.h" +#include "i830.h" +#include "i830_display.h" + +enum tv_type { + TV_TYPE_UNKNOWN, + TV_TYPE_COMPOSITE, + TV_TYPE_SVIDEO, + TV_TYPE_COMPONENT +}; + +/** Private structure for the integrated TV support */ +struct i830_tv_priv { + CARD32 save_TV_H_CTL_1; + CARD32 save_TV_H_CTL_2; + CARD32 save_TV_H_CTL_3; + CARD32 save_TV_V_CTL_1; + CARD32 save_TV_V_CTL_2; + CARD32 save_TV_V_CTL_3; + CARD32 save_TV_V_CTL_4; + CARD32 save_TV_V_CTL_5; + CARD32 save_TV_V_CTL_6; + CARD32 save_TV_V_CTL_7; + CARD32 save_TV_SC_CTL_1, save_TV_SC_CTL_2, save_TV_SC_CTL_3; + CARD32 save_TV_DAC; + CARD32 save_TV_CTL; +}; + +enum burst_modes { + TV_SC_NTSC_MJ, + TV_SC_PAL, + TV_SC_PAL_NC, + TV_SC_PAL_M, + TV_SC_NTSC_443 +}; + +const struct tv_sc_mode { + char *name; + int dda2_size, dda3_size, dda1_inc, dda2_inc, dda3_inc; + CARD32 sc_reset; + Bool pal_burst; +} tv_sc_modes[] = { + [TV_SC_NTSC_MJ] = { + "NTSC M/J", + 27456, 0, 135, 20800, 0, + TV_SC_RESET_EVERY_4, + FALSE + }, + [TV_SC_PAL] = { + "PAL", + 27648, 625, 168, 4122, 67, + TV_SC_RESET_EVERY_8, + TRUE + }, + [TV_SC_PAL_NC] = { + "PAL Nc", + 27648, 625, 135, 23578, 134, + TV_SC_RESET_EVERY_8, + TRUE + }, + [TV_SC_PAL_M] = { + "PAL M", + 27456, 0, 135, 16704, 0, + TV_SC_RESET_EVERY_8, + TRUE + }, + [TV_SC_NTSC_443] = { + "NTSC-4.43", + 27456, 525, 168, 4093, 310, + TV_SC_RESET_NEVER, + FALSE + }, +}; + +/** + * Register programming values for TV modes. + * + * These values account for -1s required. + */ +const struct tv_mode { + char *name; + CARD32 oversample; + int hsync_end, hblank_end, hblank_start, htotal; + Bool progressive; + int vsync_start_f1, vsync_start_f2, vsync_len; + Bool veq_ena; + int veq_start_f1, veq_start_f2, veq_len; + int vi_end_f1, vi_end_f2, nbr_end; + Bool burst_ena; + int hburst_start, hburst_len; + int vburst_start_f1, vburst_end_f1; + int vburst_start_f2, vburst_end_f2; + int vburst_start_f3, vburst_end_f3; + int vburst_start_f4, vburst_end_f4; +} tv_modes[] = { + { + "480i", + TV_OVERSAMPLE_8X, + 64, 124, 836, 857, + FALSE, + 6, 7, 6, + TRUE, 0, 1, 18, + 20, 21, 240, + TRUE, + 72, 34, 9, 240, 10, 240, 9, 240, 10, 240 + } +}; + + +static int +i830_tv_detect_type(ScrnInfoPtr pScrn, I830OutputPtr output) +{ + CARD32 save_tv_ctl, save_tv_dac; + CARD32 tv_ctl, tv_dac; + I830Ptr pI830 = I830PTR(pScrn); + + save_tv_ctl = INREG(TV_CTL); + save_tv_dac = INREG(TV_DAC); + + /* First, we have to disable the encoder but source from the right pipe, + * which is already enabled. + */ + tv_ctl = INREG(TV_CTL) & ~(TV_ENC_ENABLE | TV_ENC_PIPEB_SELECT); + if (output->pipe == 1) + tv_ctl |= TV_ENC_PIPEB_SELECT; + OUTREG(TV_CTL, tv_ctl); + + /* Then set the voltage overrides. */ + tv_dac = DAC_CTL_OVERRIDE | DAC_A_0_7_V | DAC_B_0_7_V | DAC_C_0_7_V; + OUTREG(TV_DAC, tv_dac); + + /* Enable sensing of the load. */ + tv_ctl |= TV_TEST_MODE_MONITOR_DETECT; + OUTREG(TV_CTL, tv_ctl); + + tv_dac |= TVDAC_STATE_CHG_EN | TVDAC_A_SENSE_CTL | TVDAC_B_SENSE_CTL | + TVDAC_C_SENSE_CTL; + OUTREG(TV_DAC, tv_dac); + + /* Wait for things to take effect. */ + i830WaitForVblank(pScrn); + + tv_dac = INREG(TV_DAC); + + OUTREG(TV_DAC, save_tv_dac); + OUTREG(TV_CTL, save_tv_ctl); + + if ((tv_dac & TVDAC_SENSE_MASK) == (TVDAC_B_SENSE | TVDAC_C_SENSE)) { + xf86DrvMsg(pScrn->scrnIndex, X_INFO, + "Detected Composite TV connection\n"); + return TV_TYPE_COMPOSITE; + } else if ((tv_dac & TVDAC_SENSE_MASK) == TVDAC_A_SENSE) { + xf86DrvMsg(pScrn->scrnIndex, X_INFO, + "Detected S-Video TV connection\n"); + return TV_TYPE_SVIDEO; + } else if ((tv_dac & TVDAC_SENSE_MASK) == 0) { + xf86DrvMsg(pScrn->scrnIndex, X_INFO, + "Detected Component TV connection\n"); + return TV_TYPE_COMPONENT; + } else { + xf86DrvMsg(pScrn->scrnIndex, X_INFO, + "Couldn't detect TV connection\n"); + return TV_TYPE_UNKNOWN; + } +} + +static void +i830_tv_dpms(ScrnInfoPtr pScrn, I830OutputPtr output, int mode) +{ + I830Ptr pI830 = I830PTR(pScrn); + + switch(mode) { + case DPMSModeOn: + OUTREG(TV_CTL, INREG(TV_CTL) | TV_ENC_ENABLE); + break; + case DPMSModeStandby: + case DPMSModeSuspend: + case DPMSModeOff: + OUTREG(TV_CTL, INREG(TV_CTL) & ~TV_ENC_ENABLE); + break; + } +} + +static void +i830_tv_save(ScrnInfoPtr pScrn, I830OutputPtr output) +{ + I830Ptr pI830 = I830PTR(pScrn); + struct i830_tv_priv *dev_priv = output->dev_priv; + + dev_priv->save_TV_H_CTL_1 = INREG(TV_H_CTL_1); + dev_priv->save_TV_H_CTL_2 = INREG(TV_H_CTL_2); + dev_priv->save_TV_H_CTL_3 = INREG(TV_H_CTL_3); + dev_priv->save_TV_V_CTL_1 = INREG(TV_V_CTL_1); + dev_priv->save_TV_V_CTL_2 = INREG(TV_V_CTL_2); + dev_priv->save_TV_V_CTL_3 = INREG(TV_V_CTL_3); + dev_priv->save_TV_V_CTL_4 = INREG(TV_V_CTL_4); + dev_priv->save_TV_V_CTL_5 = INREG(TV_V_CTL_5); + dev_priv->save_TV_V_CTL_6 = INREG(TV_V_CTL_6); + dev_priv->save_TV_V_CTL_7 = INREG(TV_V_CTL_7); + dev_priv->save_TV_SC_CTL_1 = INREG(TV_SC_CTL_1); + dev_priv->save_TV_SC_CTL_2 = INREG(TV_SC_CTL_2); + dev_priv->save_TV_SC_CTL_3 = INREG(TV_SC_CTL_3); + + dev_priv->save_TV_DAC = INREG(TV_DAC); + dev_priv->save_TV_CTL = INREG(TV_CTL); +} + +static void +i830_tv_restore(ScrnInfoPtr pScrn, I830OutputPtr output) +{ + I830Ptr pI830 = I830PTR(pScrn); + struct i830_tv_priv *dev_priv = output->dev_priv; + + OUTREG(TV_H_CTL_1, dev_priv->save_TV_H_CTL_1); + OUTREG(TV_H_CTL_2, dev_priv->save_TV_H_CTL_2); + OUTREG(TV_H_CTL_3, dev_priv->save_TV_H_CTL_3); + OUTREG(TV_V_CTL_1, dev_priv->save_TV_V_CTL_1); + OUTREG(TV_V_CTL_2, dev_priv->save_TV_V_CTL_2); + OUTREG(TV_V_CTL_3, dev_priv->save_TV_V_CTL_3); + OUTREG(TV_V_CTL_4, dev_priv->save_TV_V_CTL_4); + OUTREG(TV_V_CTL_5, dev_priv->save_TV_V_CTL_5); + OUTREG(TV_V_CTL_6, dev_priv->save_TV_V_CTL_6); + OUTREG(TV_V_CTL_7, dev_priv->save_TV_V_CTL_7); + OUTREG(TV_SC_CTL_1, dev_priv->save_TV_SC_CTL_1); + OUTREG(TV_SC_CTL_2, dev_priv->save_TV_SC_CTL_2); + OUTREG(TV_SC_CTL_3, dev_priv->save_TV_SC_CTL_3); + + OUTREG(TV_DAC, dev_priv->save_TV_DAC); + OUTREG(TV_CTL, dev_priv->save_TV_CTL); +} + +static int +i830_tv_mode_valid(ScrnInfoPtr pScrn, I830OutputPtr output, + DisplayModePtr pMode) +{ + return MODE_OK; +} + +static void +i830_tv_pre_set_mode(ScrnInfoPtr pScrn, I830OutputPtr output, + DisplayModePtr pMode) +{ + I830Ptr pI830 = I830PTR(pScrn); + + /* Disable the encoder while we set up the pipe. */ + OUTREG(TV_CTL, INREG(TV_CTL) & ~TV_ENC_ENABLE); +} + +static void +i830_tv_post_set_mode(ScrnInfoPtr pScrn, I830OutputPtr output, + DisplayModePtr pMode) +{ + I830Ptr pI830 = I830PTR(pScrn); + enum tv_type type; + const struct tv_mode *tv_mode; + const struct tv_sc_mode *sc_mode; + CARD32 tv_ctl, tv_filter_ctl; + CARD32 hctl1, hctl2, hctl3; + CARD32 vctl1, vctl2, vctl3, vctl4, vctl5, vctl6, vctl7; + CARD32 scctl1, scctl2, scctl3; + + /* Need to actually choose or construct the appropriate + * mode. For now, just set the first one in the list, with + * NTSC format. + */ + tv_mode = &tv_modes[0]; + sc_mode = &tv_sc_modes[TV_SC_NTSC_MJ]; + + type = i830_tv_detect_type(pScrn, output); + if (type == TV_TYPE_UNKNOWN) { + xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "Defaulting TV to SVIDEO\n"); + type = TV_TYPE_SVIDEO; + } + + hctl1 = (tv_mode->hsync_end << TV_HSYNC_END_SHIFT) | + (tv_mode->htotal << TV_HTOTAL_SHIFT); + + hctl2 = (tv_mode->hburst_start << 16) | + (tv_mode->hburst_len << TV_HBURST_LEN_SHIFT); + if (tv_mode->burst_ena) + hctl2 |= TV_BURST_ENA; + + hctl3 = (tv_mode->hblank_start << TV_HBLANK_START_SHIFT) | + (tv_mode->hblank_end << TV_HBLANK_END_SHIFT); + + vctl1 = (tv_mode->nbr_end << TV_NBR_END_SHIFT) | + (tv_mode->vi_end_f1 << TV_VI_END_F1_SHIFT) | + (tv_mode->vi_end_f2 << TV_VI_END_F2_SHIFT); + + vctl2 = (tv_mode->vsync_len << TV_VSYNC_LEN_SHIFT) | + (tv_mode->vsync_start_f1 << TV_VSYNC_START_F1_SHIFT) | + (tv_mode->vsync_start_f2 << TV_VSYNC_START_F2_SHIFT); + + vctl3 = (tv_mode->veq_len << TV_VEQ_LEN_SHIFT) | + (tv_mode->veq_start_f1 << TV_VEQ_START_F1_SHIFT) | + (tv_mode->veq_start_f2 << TV_VEQ_START_F2_SHIFT); + if (tv_mode->veq_ena) + vctl3 |= TV_EQUAL_ENA; + + vctl4 = (tv_mode->vburst_start_f1 << TV_VBURST_START_F1_SHIFT) | + (tv_mode->vburst_end_f1 << TV_VBURST_END_F1_SHIFT); + + vctl5 = (tv_mode->vburst_start_f2 << TV_VBURST_START_F2_SHIFT) | + (tv_mode->vburst_end_f2 << TV_VBURST_END_F2_SHIFT); + + vctl6 = (tv_mode->vburst_start_f3 << TV_VBURST_START_F3_SHIFT) | + (tv_mode->vburst_end_f3 << TV_VBURST_END_F3_SHIFT); + + vctl7 = (tv_mode->vburst_start_f4 << TV_VBURST_START_F4_SHIFT) | + (tv_mode->vburst_end_f4 << TV_VBURST_END_F4_SHIFT); + + tv_ctl = TV_ENC_ENABLE; + if (output->pipe == 1) + tv_ctl |= TV_ENC_PIPEB_SELECT; + + switch (type) { + case TV_TYPE_COMPOSITE: + tv_ctl |= TV_ENC_OUTPUT_COMPOSITE; + break; + case TV_TYPE_COMPONENT: + tv_ctl |= TV_ENC_OUTPUT_COMPONENT; + break; + default: + case TV_TYPE_SVIDEO: + tv_ctl |= TV_ENC_OUTPUT_SVIDEO; + break; + } + tv_ctl |= tv_mode->oversample; + if (tv_mode->progressive) + tv_ctl |= TV_PROGRESSIVE; + if (sc_mode->pal_burst) + tv_ctl |= TV_PAL_BURST; + + scctl1 = TV_SC_DDA1_EN | TV_SC_DDA1_EN; + if (sc_mode->dda3_size != 0) + scctl1 |= TV_SC_DDA3_EN; + scctl1 |= sc_mode->sc_reset; + /* XXX: set the burst level */ + scctl1 |= sc_mode->dda1_inc << TV_SCDDA1_INC_SHIFT; + + scctl2 = sc_mode->dda2_size << TV_SCDDA2_SIZE_SHIFT | + sc_mode->dda2_inc << TV_SCDDA2_INC_SHIFT; + + scctl3 = sc_mode->dda3_size << TV_SCDDA3_SIZE_SHIFT | + sc_mode->dda3_inc << TV_SCDDA3_INC_SHIFT; + + /* Enable two fixes for the chips that need them. */ + if (pI830->PciInfo->chipType < PCI_CHIP_I945_G) + tv_ctl |= TV_ENC_C0_FIX | TV_ENC_SDP_FIX; + + tv_filter_ctl = TV_AUTO_SCALE; + if (pMode->HDisplay > 1024) + tv_ctl |= TV_V_FILTER_BYPASS; + + OUTREG(TV_H_CTL_1, hctl1); + OUTREG(TV_H_CTL_2, hctl2); + OUTREG(TV_H_CTL_3, hctl3); + OUTREG(TV_V_CTL_1, vctl1); + OUTREG(TV_V_CTL_2, vctl2); + OUTREG(TV_V_CTL_3, vctl3); + OUTREG(TV_V_CTL_4, vctl4); + OUTREG(TV_V_CTL_5, vctl5); + OUTREG(TV_V_CTL_6, vctl6); + OUTREG(TV_V_CTL_7, vctl7); + OUTREG(TV_SC_CTL_1, scctl1); + OUTREG(TV_SC_CTL_2, scctl2); + OUTREG(TV_SC_CTL_3, scctl3); + + OUTREG(TV_DAC, 0); + OUTREG(TV_CTL, tv_ctl); +} + +void +i830_tv_init(ScrnInfoPtr pScrn) +{ + I830Ptr pI830 = I830PTR(pScrn); + + if ((INREG(TV_CTL) & TV_FUSE_STATE_MASK) == TV_FUSE_STATE_DISABLED) + return; + + pI830->output[pI830->num_outputs].dev_priv = + malloc(sizeof(struct i830_tv_priv)); + if (pI830->output[pI830->num_outputs].dev_priv == NULL) + return; + + pI830->output[pI830->num_outputs].type = I830_OUTPUT_ANALOG; + pI830->output[pI830->num_outputs].dpms = i830_tv_dpms; + pI830->output[pI830->num_outputs].save = i830_tv_save; + pI830->output[pI830->num_outputs].restore = i830_tv_restore; + 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->num_outputs++; +} |