Add work-in-progress integrated TV-out support.

This is the TV connector on board for the 915GM and 945GM.

It is currently not hooked up to output initialization as it's entirely
untested.  However, I think this is a reasonable starting point for getting
TV-out actually working.
This commit is contained in:
Eric Anholt 2006-10-23 14:30:38 -07:00
parent 8149681f2e
commit a91c0cbab5
4 changed files with 999 additions and 0 deletions

View File

@ -77,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 \

View File

@ -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

View File

@ -279,6 +279,8 @@ struct _I830OutputRec {
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 {
@ -693,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.

430
src/i830_tv.c Normal file
View File

@ -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++;
}