--- src/i810_reg.h | 13 ++- src/i830.h | 3 src/i830_hdmi.c | 64 +++++++++++++++ src/i830_modes.c | 189 +++++++++++++++++++++++++++++++++++++++++++++ src/i830_sdvo.c | 49 ++++++++++- src/i830_sdvo.h | 2 6 files changed, 313 insertions(+), 7 deletions(-) --- xf86-video-intel.orig/src/i810_reg.h +++ xf86-video-intel/src/i810_reg.h @@ -1240,6 +1240,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN # define HDMID_HOTPLUG_INT_EN (1 << 27) # define SDVOB_HOTPLUG_INT_EN (1 << 26) # define SDVOC_HOTPLUG_INT_EN (1 << 25) +# define AUDIO_HOTPLUG_INT_EN (1 << 24) # define TV_HOTPLUG_INT_EN (1 << 18) # define CRT_HOTPLUG_INT_EN (1 << 9) # define CRT_HOTPLUG_ACTIVATION_PERIOD_32 (0 << 8) @@ -1271,7 +1272,17 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN # define CRT_HOTPLUG_MONITOR_NONE (0 << 8) # define SDVOC_HOTPLUG_INT_STATUS (1 << 7) # define SDVOB_HOTPLUG_INT_STATUS (1 << 6) - +#define AUDIO_VENDOR_DEVICE_ID 0x62020 +#define INTEL_AUDIO_DEVCL 0x808629FB +#define INTEL_AUDIO_DEVBLC 0x80862801 +#define INTEL_AUDIO_DEVCTG 0x80862802 + +#define AUDIO_CNTL_STATUS 0x620B4 +#define AUDIO_ELD_VALID_DEVCL_DEVBLC (1 << 13) +#define AUDIO_ELD_VALID_DEVCTG (1 << 14) +#define AUDIO_ELD_ADDR (0xf << 5) +#define AUDIO_ELD_ACK (1 << 4) +#define AUDIO_HDMIW_HDMIEDID 0x6210C #define SDVOB 0x61140 #define SDVOC 0x61160 #define SDVO_ENABLE (1 << 31) --- xf86-video-intel.orig/src/i830.h +++ xf86-video-intel/src/i830.h @@ -887,7 +887,8 @@ Bool I830UnbindAGPMemory(ScrnInfoPtr pSc /* i830_modes.c */ DisplayModePtr i830_ddc_get_modes(xf86OutputPtr output); - +int i830_handle_cea_like_data(xf86OutputPtr output,Uchar **buf); +unsigned long i830_extract_max_tmds_clock(xf86OutputPtr output); /* i830_tv.c */ void i830_tv_init(ScrnInfoPtr pScrn); --- xf86-video-intel.orig/src/i830_hdmi.c +++ xf86-video-intel/src/i830_hdmi.c @@ -45,12 +45,17 @@ struct i830_hdmi_priv { static int i830_hdmi_mode_valid(xf86OutputPtr output, DisplayModePtr mode) { + unsigned long tmds_max_clock = i830_extract_max_tmds_clock(output); + if (mode->Clock > 165000) return MODE_CLOCK_HIGH; if (mode->Clock < 20000) return MODE_CLOCK_LOW; + if( 0 < tmds_max_clock && tmds_max_clock < mode->Clock) + return MODE_CLOCK_HIGH; + return MODE_OK; } @@ -64,6 +69,60 @@ i830_hdmi_mode_fixup(xf86OutputPtr outpu return TRUE; } +static uint32_t i830_hdmi_get_eld_flag(I830Ptr pI830 ) +{ + uint32_t ar ; + + ar = INREG(AUDIO_VENDOR_DEVICE_ID); + + if (INTEL_AUDIO_DEVBLC == ar || INTEL_AUDIO_DEVCL == ar) { + ar = AUDIO_ELD_VALID_DEVCL_DEVBLC; + } else { + ar = AUDIO_ELD_VALID_DEVCTG; + } + + return ar; + +} + +static void i830_hdmi_set_edid_like_data(xf86OutputPtr output) +{ + I830Ptr pI830 = I830PTR(output->scrn); + uint32_t *eld; + int eld_len; + uint32_t ar; + uint32_t flag; + int i; + + + eld = NULL; + eld_len = i830_extract_cea_like_data(output, (Uchar **)&eld); + eld_len = eld_len / sizeof(uint32_t); + + if (NULL == eld) { + xf86DrvMsg(output->scrn->scrnIndex, X_ERROR, + "HDMI failed to get ELD \n"); + goto end; + } + + flag = i830_hdmi_get_eld_flag(pI830); + + ar = INREG(AUDIO_CNTL_STATUS); + ar &= ~(flag | AUDIO_ELD_ADDR); + OUTREG(AUDIO_CNTL_STATUS, ar); + + for (i = 0; i < eld_len; i = i + 1) + OUTREG(AUDIO_HDMIW_HDMIEDID, eld[i]); + + ar = INREG(AUDIO_CNTL_STATUS); + ar |= flag ; + OUTREG(AUDIO_CNTL_STATUS, ar); + +end: + if (NULL != eld ) + xfree(eld); +} + static void i830_hdmi_mode_set(xf86OutputPtr output, DisplayModePtr mode, DisplayModePtr adjusted_mode) @@ -76,6 +135,8 @@ i830_hdmi_mode_set(xf86OutputPtr output, I830CrtcPrivatePtr intel_crtc = crtc->driver_private; uint32_t sdvox; + i830_hdmi_set_edid_like_data(output); + sdvox = SDVO_ENCODING_HDMI | SDVO_BORDER_ENABLE | SDVO_VSYNC_ACTIVE_HIGH | @@ -165,7 +226,8 @@ i830_hdmi_detect(xf86OutputPtr output) temp | HDMIB_HOTPLUG_INT_EN | HDMIC_HOTPLUG_INT_EN | - HDMID_HOTPLUG_INT_EN); + HDMID_HOTPLUG_INT_EN | + AUDIO_HOTPLUG_INT_EN); POSTING_READ(PORT_HOTPLUG_EN); --- xf86-video-intel.orig/src/i830_modes.c +++ xf86-video-intel/src/i830_modes.c @@ -54,6 +54,195 @@ #include "xf86Modes.h" #include +#define CEA_LIKE_DATA_MAX_LEN 48 +struct eld_header{ + Uchar rsv_0 :3; + Uchar ELD_ver :5; + Uchar rsv_1; + Uchar baseline_ELD_len; + Uchar rsv_3; +}__attribute__ ((packed)); + +struct eld_data_fixed_fields { + struct eld_header header; + + /* byte 1 */ + Uchar MNL :5; + Uchar CEA_EDID_ver :3; + + /* byte 2 */ + Uchar HDCP :1; + Uchar S_AI :1; + Uchar Conn_Type :2; + Uchar SAD_Count :4; + + /* byte 3 */ + Uchar Aud_Synch_Delay; + + /* byte 4 */ + Uchar FLR :1; + Uchar LFE :1; + Uchar FC :1; + Uchar RLR :1; + Uchar RC :1; + Uchar FLRC :1; + Uchar RLRC :1; + Uchar rsv_7 :1; + + /* byte 5-12 */ + Uchar Port_ID[8]; /* little endian */ + + /* byte 13-14 */ + Uchar Manufacture_Name[2]; + /* byte 15-16 */ + Uchar Product_Code[2]; + + /* byte 17 len MNL */ + char Monitor_Name[0]; +} __attribute__ ((packed)); + +unsigned long i830_extract_max_tmds_clock(xf86OutputPtr output) +{ + struct cea_data_blk *blk ; + struct extension_type type; + + unsigned long ret = 0; + + if (0 == (EDID_CEA_EXTENSION_FLG & output->MonInfo->flags)) { + goto end; + } + + type.body_type = CEA_EXT; + type.data_type = CEA_VENDOR_BLK; + blk = (struct cea_data_blk *)xf86DDCGetCEA( output->MonInfo,&type); + + if (NULL != blk) { + if (VENDOR_OFFSET(struct cea_vendor_blk, hdmi.Max_TMDS_Clock) <= + blk->len) { + ret = blk->u.vendor.hdmi.Max_TMDS_Clock * HDMI_MAX_TMDS_UNIT; + } + } +end: + return ret; +} + +int i830_extract_cea_like_data(xf86OutputPtr output, Uchar **buf) +{ + struct eld_data_fixed_fields *eld; + struct cea_data_blk *blk ; + struct cea_audio_blk *audio; + struct extension_type type; + Uchar *name ; + int i; + int eld_len; + int SAD_Count; + + + if (0 == (EDID_CEA_EXTENSION_FLG & output->MonInfo->flags)) { + eld = NULL; + eld_len = 0; + goto end; + } + + name = NULL; + audio = NULL; + + eld_len = sizeof(struct eld_data_fixed_fields); + + type.body_type = CEA_EXT; + type.data_type = CEA_AUDIO_BLK; + blk = (struct cea_data_blk *)xf86DDCGetCEA(output->MonInfo, &type); + + SAD_Count = 0; + if (NULL != blk) { + audio = (struct cea_audio_blk *)&blk->u.audio; + eld_len = eld_len + blk->len; + SAD_Count = blk->len/3; // SAD_Count is multiple of 3 bytes + } + + for (i=0; iMonInfo->det_mon_num; i++) { + if (0xfc == output->MonInfo->det_mon[i].type){ + eld_len = eld_len + EDID_DET_NAME_LEN ; + name = output->MonInfo->det_mon[i].section.name; + break; + } + } + /* The item need to be multiple of 8 because sdvo write 8 bytes one time */ + eld_len = (eld_len + 7)/8; + eld_len = eld_len * 8; + eld = (struct eld_data_fixed_fields *)xcalloc(1, eld_len); + + if (NULL == eld) { + eld_len = 0; + goto end; + } + + memset((Uchar *)eld, 0, eld_len); + /* The item need to be multiple of 4 */ + eld->header.baseline_ELD_len = (eld_len - sizeof(struct eld_header))/4; + eld->header.ELD_ver = CEA_EXT; + eld->HDCP = 0; + eld->CEA_EDID_ver = output->MonInfo->ver.version; + if (NULL != name) { + eld->MNL = EDID_DET_NAME_LEN; + } + eld->SAD_Count = SAD_Count; + type.body_type = CEA_EXT; + type.data_type = CEA_VENDOR_BLK; + blk = (struct cea_data_blk *)xf86DDCGetCEA( output->MonInfo,&type); + + eld->Conn_Type = 0; + /* Currently do not consider about Interlaced mode. + * Because the member hdmi of vendor data block is extension part, + * it's length is flexible, we need to guarantee the boundary + * prior to fetching data. + */ + + if (NULL != blk) { + if (VENDOR_OFFSET(struct cea_vendor_blk, hdmi.Support_flags) <= + blk->len) + eld->S_AI = VENDOR_SUPPORT_AI(blk->u.vendor.hdmi.Support_flags); + + if (VENDOR_OFFSET(struct cea_vendor_blk, hdmi.Audio_Latency) <= + blk->len) { + if (VENDOR_LATENCY_PRESENT(blk->u.vendor.hdmi.Latency_Present)) { + if(blk->u.vendor.hdmi.Audio_Latency) + eld->Aud_Synch_Delay = blk->u.vendor.hdmi.Audio_Latency - 1; + } + } + } + + type.body_type = CEA_EXT; + type.data_type = CEA_SPEAKER_ALLOC_BLK; + blk = (struct cea_data_blk *)xf86DDCGetCEA(output->MonInfo,&type); + if (NULL != blk) { + eld->FLR = blk->u.speaker.FLR; + eld->LFE = blk->u.speaker.LFE; + eld->FC = blk->u.speaker.FC; + eld->RLR = blk->u.speaker.RLR; + eld->RC = blk->u.speaker.RC; + eld->FLRC = blk->u.speaker.FLRC; + eld->RLRC = blk->u.speaker.RLRC; + } + + //eld->Port_ID /*TBD XXX*/ + eld->Manufacture_Name[0] = output->MonInfo->vendor.name[0]; + eld->Manufacture_Name[1] = output->MonInfo->vendor.name[1]; + eld->Product_Code[0] = output->MonInfo->vendor.prod_id & 0xff; + eld->Product_Code[1] = output->MonInfo->vendor.prod_id >> 8; + + if (NULL != name) { + memcpy(eld->Monitor_Name, name, EDID_DET_NAME_LEN); + } + if (NULL != audio) { + memcpy(eld->Monitor_Name + EDID_DET_NAME_LEN, audio,SAD_Count * 3); + } + +end: + *buf = eld; + return eld_len <= CEA_LIKE_DATA_MAX_LEN ? eld_len:CEA_LIKE_DATA_MAX_LEN ; +} + DisplayModePtr i830_ddc_get_modes (xf86OutputPtr output) { --- xf86-video-intel.orig/src/i830_sdvo.c +++ xf86-video-intel/src/i830_sdvo.c @@ -36,6 +36,7 @@ USE OR OTHER DEALINGS IN THE SOFTWARE. * this code doesn't deal with either ganged mode or more than one SDVO output. */ + #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -889,10 +890,13 @@ struct dip_infoframe { union { struct { /* Packet Byte #1 */ - uint8_t S:2; - uint8_t B:2; + uint8_t S0: 1; + uint8_t S1:1; + uint8_t B0:1; + uint8_t B1:1; uint8_t A:1; - uint8_t Y:2; + uint8_t Y0:1; + uint8_t Y1:1; uint8_t rsvd1:1; /* Packet Byte #2 */ uint8_t R:4; @@ -954,6 +958,36 @@ static void i830_sdvo_set_avi_infoframe( SDVO_HBUF_TX_VSYNC); } +static void i830_sdvo_set_edid_like_data(xf86OutputPtr output) +{ + Uchar *eld; + int eld_len; + Uchar av_split; + Uchar eld_pre; + + eld = NULL; + eld_len = i830_extract_cea_like_data(output, &eld); + if (NULL == eld) + goto end; + + av_split = 7; + + i830_sdvo_write_cmd(output, SDVO_CMD_SET_HBUF_AV_SPLIT, + &av_split, sizeof(Uchar)); + av_split = 0; + i830_sdvo_set_hdmi_buf(output, av_split, eld, eld_len, + SDVO_HBUF_TX_DISABLED); + + eld_pre = SDVO_ELD_PRESENT | SDVO_ELD_VALID; + + i830_sdvo_write_cmd(output, SDVO_CMD_SET_AUDIO_STAT, + &eld_pre, sizeof(Uchar)); +end: + if (NULL != eld) + xfree(eld); + +} + static Bool i830_sdvo_mode_fixup(xf86OutputPtr output, DisplayModePtr mode, DisplayModePtr adjusted_mode) @@ -1025,6 +1059,8 @@ i830_sdvo_mode_set(xf86OutputPtr output, struct i830_sdvo_dtd input_dtd; uint8_t status; + sdvox = 0; + if (!mode) return; @@ -1043,6 +1079,7 @@ i830_sdvo_mode_set(xf86OutputPtr output, if (dev_priv->is_hdmi) { i830_sdvo_set_avi_infoframe(output, mode); + i830_sdvo_set_edid_like_data(output); sdvox |= SDVO_AUDIO_ENABLE; } @@ -1118,7 +1155,7 @@ i830_sdvo_mode_set(xf86OutputPtr output, } else { sdvox |= (sdvo_pixel_multiply - 1) << SDVO_PORT_MULTIPLY_SHIFT; } - + sdvox = sdvox ; i830_sdvo_write_sdvox(output, sdvox); } @@ -1264,6 +1301,7 @@ i830_sdvo_mode_valid(xf86OutputPtr outpu { I830OutputPrivatePtr intel_output = output->driver_private; struct i830_sdvo_priv *dev_priv = intel_output->dev_priv; + unsigned long tmds_max_clock = i830_extract_max_tmds_clock(output); if (pMode->Flags & V_DBLSCAN) return MODE_NO_DBLESCAN; @@ -1274,6 +1312,9 @@ i830_sdvo_mode_valid(xf86OutputPtr outpu if (dev_priv->pixel_clock_max < pMode->Clock) return MODE_CLOCK_HIGH; + if(0 != dev_priv->is_hdmi && 0 < tmds_max_clock && tmds_max_clock < pMode->Clock) + return MODE_CLOCK_HIGH; + return MODE_OK; } --- xf86-video-intel.orig/src/i830_sdvo.h +++ xf86-video-intel/src/i830_sdvo.h @@ -24,6 +24,8 @@ * Eric Anholt * */ +#define SDVO_ELD_PRESENT (1 << 0) +#define SDVO_ELD_VALID (1 << 1) Bool i830_sdvo_init(ScrnInfoPtr pScrn, int output_device);