📄 sii9031.c
字号:
/* * * Copyright (c) 2001-2007 Sigma Designs, Inc. * All Rights Reserved. Proprietary and Confidential. * *//** @file SiI9031.c @brief Interface to access the SiI9031 HDMI capture chip @author Christian Wolff, Sean.Sekwon.Choi @date 2007-07-16*/// to enable or disable the debug messages of this source file, put 1 or 0 below#if 1#define LOCALDBG ENABLE#else#define LOCALDBG DISABLE#endif#include "common.h"#include "hdmi.h"#include "SiI9031.h"#include "i2c.h"struct cap_SiI9031_instance { // Access struct RUA *pRUA; RMuint32 I2CModuleID; struct EMhwlibI2CDeviceParameter I2CDevice; RMuint8 BaseDevice; // State RMbool verbouse; RMuint8 intr_mask[6]; RMbool intr_debug; RMbool audio_enable; RMuint32 mclk_factor; RMuint32 cable_eq; RMbool upsample_from_422; enum EMhwlibTVStandard TVStandard; enum EMhwlibColorSpace InputColorSpace; enum cap_hdmi_mode hdmi_mode; RMbool last_auth; RMbool last_crypt; RMbool last_hdcp_ok; RMbool break_hdcp; RMbool last_power; RMbool last_clock; RMbool last_sync; RMbool last_hotplug; RMbool audio_pll_locked; enum audio_state audio_state; RMbool update_videomode; RMuint64 audio_guard_time;};/* CEA 861-C/D subset of all EMhwlib video modes *//* translates VIC to enum EMhwlibTVStandard */extern const enum EMhwlibTVStandard cap_hdmi_cea861_modes[HDMI_CEA861_VIDEOMODES_N];// VESA/CVT subset of all EMhwlib video modesextern const enum EMhwlibTVStandard cap_hdmi_it_modes[];extern RMuint32 cap_hdmi_it_modes_n;RMstatus cap_SiI9031_open( struct RUA *pRUA, struct cap_SiI9031_instance **ppSiI9031, RMuint32 I2CModuleID, struct EMhwlibI2CDeviceParameter *pI2CDevice){ struct cap_SiI9031_instance *pSiI9031; RMDBGLOG((FUNCNAME, "%s\n",__func__)); // Sanity checks if (ppSiI9031 == NULL) return RM_FATALINVALIDPOINTER; *ppSiI9031 = NULL; if (pRUA == NULL) return RM_FATALINVALIDPOINTER; if (pI2CDevice == NULL) return RM_FATALINVALIDPOINTER; // Allocate and clear local instance pSiI9031 = (struct cap_SiI9031_instance *)RMMalloc(sizeof(struct cap_SiI9031_instance)); if (pSiI9031 == NULL) { RMDBGLOG((ENABLE, "[SiI9031] FATAL! Not enough memory for struct cap_SiI9031_instance!\n")); return RM_FATALOUTOFMEMORY; } RMMemset(pSiI9031, 0, sizeof(struct cap_SiI9031_instance)); *ppSiI9031 = pSiI9031; pSiI9031->audio_state = audio_state_off; // Set default and startup values pSiI9031->pRUA = pRUA; pSiI9031->I2CModuleID = I2CModuleID; pSiI9031->I2CDevice = *pI2CDevice; pSiI9031->BaseDevice = pI2CDevice->DevAddr; pSiI9031->mclk_factor = 256; pSiI9031->cable_eq = 0x0C; pSiI9031->update_videomode = TRUE; return RM_OK;}RMstatus cap_SiI9031_close( struct cap_SiI9031_instance *pSiI9031){ RMDBGLOG((FUNCNAME, "%s\n",__func__)); // Sanity checks if (pSiI9031 == NULL) return RM_OK; // already closed // Free all ressources RMFree(pSiI9031); return RM_OK;}/* tri-state all outputs and power down chip */RMstatus cap_SiI9031_tristate( struct cap_SiI9031_instance *pSiI9031){ RMDBGLOG((FUNCNAME, "%s\n",__func__)); // Sanity checks if (pSiI9031 == NULL) return RM_FATALINVALIDPOINTER; cap_i2c_open(pSiI9031, pSiI9031->BaseDevice); cap_i2c_write(pSiI9031, 0x08, 0x06); // power down SiI9031 cap_i2c_write(pSiI9031, 0x09, 0x00); // tri-state SiI9031 cap_i2c_close(pSiI9031); cap_i2c_open(pSiI9031, pSiI9031->BaseDevice + 8); cap_i2c_write(pSiI9031, 0x27, 0x01); // Disable I2S out and MClk cap_i2c_write(pSiI9031, 0x29, 0x18); // Disable I2S signals cap_i2c_write(pSiI9031, 0x3C, 0x00); // power down and tri-state chip. cap_i2c_write(pSiI9031, 0x3E, 0x00); // power down and tri-state chip. cap_i2c_write(pSiI9031, 0x3F, 0x00); // power down and tri-state chip. cap_i2c_close(pSiI9031); return RM_OK;}/* Compare two values and return a weighted match value. Return value is 'weight' if both values are equal, or 50% of 'weight' minus the percentage points 'a' is away from 'b'. */static inline RMuint32 comp_weight(RMuint32 a, RMuint32 b, RMuint32 weight) { RMuint32 dist; if (a == b) return weight; dist = (a > b) ? a - b : b - a;// weight /= 2; dist = (dist * 100) / b; return (weight > dist) ? weight - dist : 0;}/* detect video format from the line and pixel counter in the SiI9031 */RMstatus cap_SiI9031_measure_video_format( struct cap_SiI9031_instance *pSiI9031, RMuint32 *pVIC, // VIC of the detected mode RMbool *UseStandard, // TRUE: valid information in pTVStandard, FALSE: valid info only in pTVFormat enum EMhwlibTVStandard *pTVStandard, struct EMhwlibTVFormatDigital *pTVFormat, RMuint32 *pAspX, RMuint32 *pAspY){ RMstatus err; RMuint8 reg; RMuint8 data[17]; RMuint32 h_res, v_res, de_pix, de_lin, vid_vtavl, vid_vfp, vid_hfp, vid_hswidth, vid_xpcnt, pixclk, pixel_rep, h_shift; RMbool intrl, vspol, hspol; RMuint32 i, best_match; enum EMhwlibTVStandard selected, mode, old; RMDBGLOG((FUNCNAME, "%s\n",__func__)); // Sanity checks if (pSiI9031 == NULL) return RM_FATALINVALIDPOINTER; if (UseStandard) *UseStandard = TRUE; if (pVIC) *pVIC = 0; selected = old = (pTVStandard) ? *pTVStandard : EMhwlibTVStandard_Custom; /* read measurement registers from SiI9031 */ cap_i2c_read_dev(pSiI9031, pSiI9031->BaseDevice, 0x06, ®); if ((reg & 0x03) != 0x03) { RMDBGLOG((ENABLE, "[SiI9031] no %s%s%s!\n", (reg & 0x08) ? "" : "power", (reg & 0x02) ? "" : (reg & 0x08) ? "clock" : (reg & 0x01) ? " or clock" : ", clock", (reg & 0x01) ? "" : ((reg & 0x02) && (reg & 0x08)) ? "sync" : " or sync")); return RM_ERROR; } cap_i2c_read_dev(pSiI9031, pSiI9031->BaseDevice, 0x08, ®); pixel_rep = RMunshiftBits(reg, 2, 6); h_shift = (pixel_rep > 1) ? 2 : pixel_rep; cap_i2c_read_data(pSiI9031, pSiI9031->BaseDevice, 0x3A, &data[0], 4); cap_i2c_read_data(pSiI9031, pSiI9031->BaseDevice, 0x4E, &data[4], 8); cap_i2c_read_data(pSiI9031, pSiI9031->BaseDevice, 0x59, &data[12], 4); cap_i2c_read_data(pSiI9031, pSiI9031->BaseDevice, 0x6F, &data[16], 1); h_res = (data[0] & 0xFF); h_res |= ((data[1] & 0x1F) << 8); v_res = (data[2] & 0xFF); v_res |= ((data[3] & 0x07) << 8); de_pix = (data[4] & 0xFF); de_pix |= ((data[5] & 0x1F) << 8); de_lin = (data[6] & 0xFF); de_lin |= ((data[7] & 0x07) << 8); vid_vtavl = (data[8] & 0x3F); vid_vfp = (data[9] & 0x3F); intrl = (data[11] & 0x04) ? TRUE : FALSE; vspol = (data[11] & 0x02) ? TRUE : FALSE; hspol = (data[11] & 0x01) ? TRUE : FALSE; vid_hfp = (data[12] & 0xFF); vid_hswidth = (data[14] & 0xFF); vid_hswidth |= ((data[15] & 0x03) << 8); vid_xpcnt = (data[16] & 0xFF); pixclk = vid_xpcnt ? RM64mult32div32(28322000, 128, vid_xpcnt) : 0; if (pixel_rep) { h_res >>= h_shift; de_pix >>= h_shift; vid_hfp >>= h_shift; vid_hswidth >>= h_shift; vid_xpcnt >>= h_shift; pixclk >>= h_shift; } RMDBGLOG((ENABLE, "[SiI9031] - %lux%lu%c, total:%lux%lu, vsync:%lu-%lu-x-%s., hsync:%lu-%lu-%lu-%s., xpcnt:%lu (%lu Hz)\n", de_pix, de_lin * (intrl ? 2 : 1), intrl ? 'i' : 'p', h_res, intrl ? v_res * 2 + 1 : v_res, vid_vfp, vid_vtavl, vspol ? "pos" : "neg", vid_hfp, vid_hswidth, h_res - de_pix - vid_hfp - vid_hswidth, hspol ? "pos" : "neg", vid_xpcnt, pixclk)); best_match = 0; // match HDMI/CEA861 modes i = 1; for (i = 1; i < (sizeof(cap_hdmi_cea861_modes) / sizeof(enum EMhwlibTVStandard)); i++) { struct EMhwlibTVFormatDigital dig; RMuint32 match; RMbool dig_Intrl; // skip dual-aspect-ratio modes, checking for one is enough if ((i > 1) && (cap_hdmi_cea861_modes[i - 1] == cap_hdmi_cea861_modes[i])) { continue; } mode = cap_hdmi_cea861_modes[i]; err = RUAExchangeProperty(pSiI9031->pRUA, DisplayBlock, RMDisplayBlockPropertyID_TVFormatDigital, &mode, sizeof(mode), &dig, sizeof(dig)); if RMFAILED(err) { RMDBGLOG((ENABLE, "[SiI9031] Could not get format!\n")); return err; } dig_Intrl = (dig.TopFieldHeight != 0); if (dig_Intrl) dig.VTotalSize = (dig.VTotalSize + 1) / 2; match = 0; match += comp_weight(h_res, dig.HTotalSize, 20); match += comp_weight(v_res, dig.VTotalSize, 20); match += comp_weight(de_pix, dig.ActiveWidth, 20); match += comp_weight(de_lin, dig.ActiveHeight, 20); //match += comp_weight(vid_vtavl, dig.YOffsetTop, 12); //match += comp_weight(vid_vfp, dig.VTotalSize - dig.ActiveHeight - dig.YOffsetTop, 12); //match += comp_weight(vid_hfp, dig.HTotalSize - dig.ActiveWidth - dig.XOffset, 12); //match += comp_weight(vid_hswidth, dig.HSyncWidth, 12); match += comp_weight(vid_xpcnt, RM64mult32div32(28322000, 128, dig.PixelClock), 20); if (intrl == dig_Intrl) match += 32; if (vspol == ! dig.VSyncActiveLow) match += 20; if (hspol == ! dig.HSyncActiveLow) match += 20; if (match > best_match) { best_match = match; selected = mode; if (pVIC) *pVIC = i; // TODO: aspect ratio! } } // match VESA modes for (i = 0; i < cap_hdmi_it_modes_n; i++) { struct EMhwlibTVFormatDigital dig; RMuint32 match; RMbool dig_Intrl; mode = cap_hdmi_it_modes[i]; err = RUAExchangeProperty(pSiI9031->pRUA, DisplayBlock, RMDisplayBlockPropertyID_TVFormatDigital, &mode, sizeof(mode), &dig, sizeof(dig)); if RMFAILED(err) { RMDBGLOG((ENABLE, "[SiI9031] Could not get format!\n")); return err; } dig_Intrl = (dig.TopFieldHeight != 0); if (dig_Intrl) dig.VTotalSize = (dig.VTotalSize + 1) / 2; match = 0; match += comp_weight(h_res, dig.HTotalSize, 20); match += comp_weight(v_res, dig.VTotalSize, 20); match += comp_weight(de_pix, dig.ActiveWidth, 20); match += comp_weight(de_lin, dig.ActiveHeight, 20); //match += comp_weight(vid_vtavl, dig.YOffsetTop, 12); //match += comp_weight(vid_vfp, dig.VTotalSize - dig.ActiveHeight - dig.YOffsetTop, 12); //match += comp_weight(vid_hfp, dig.HTotalSize - dig.ActiveWidth - dig.XOffset, 12); //match += comp_weight(vid_hswidth, dig.HSyncWidth, 12); match += comp_weight(vid_xpcnt, RM64mult32div32(28322000, 128, dig.PixelClock), 20); if (intrl == dig_Intrl) match += 32; if (vspol == ! dig.VSyncActiveLow) match += 20; if (hspol == ! dig.HSyncActiveLow) match += 20; if (match > best_match) { best_match = match; selected = mode; if (pVIC) *pVIC = 0; } } // if (selected != old) {// GetTVStandardName(selected, &StandardName);// RMDBGLOG((ENABLE, "[SiI9031] Detected new mode from timing: %s\n", StandardName));// } if (pTVStandard) *pTVStandard = selected; if (pTVFormat) { err = RUAExchangeProperty(pSiI9031->pRUA, DisplayBlock, RMDisplayBlockPropertyID_TVFormatDigital, &selected, sizeof(selected), pTVFormat, sizeof(*pTVFormat)); } if (pAspX && pAspY) { // TODO aspect ratio! *pAspX = 0; *pAspY = 0; } return RM_OK;}/* get current audio channel status from SiI9031 */RMstatus cap_SiI9031_get_audio_channel_status( struct cap_SiI9031_instance *pSiI9031, RMuint8 ch_stat[5]){ RMDBGLOG((FUNCNAME, "%s\n",__func__)); // Sanity checks if (pSiI9031 == NULL) return RM_FATALINVALIDPOINTER; if (ch_stat == NULL) return RM_FATALINVALIDPOINTER; cap_i2c_read_data(pSiI9031, pSiI9031->BaseDevice + 8, 0x2A, &ch_stat[0], 3); cap_i2c_read_data(pSiI9031, pSiI9031->BaseDevice + 8, 0x30, &ch_stat[3], 2); RMDBGLOG((ENABLE, "[SiI9031] audio channel status [39:0]: %02X %02X %02X %02X %02X\n", ch_stat[4], ch_stat[3], ch_stat[2], ch_stat[1], ch_stat[0])); return RM_OK;}/* get current audio clock divider values N/CTS from SiI9031 */RMstatus cap_SiI9031_get_audio_n_cts( struct cap_SiI9031_instance *pSiI9031, RMuint32 *pN, RMuint32 *pCTS, RMuint32 *pApproxSamplingRate){ RMuint8 data[3]; RMuint32 n, cts; RMDBGLOG((FUNCNAME, "%s\n",__func__)); // Sanity checks if (pSiI9031 == NULL) return RM_FATALINVALIDPOINTER; // read N/CTS pair, CTS = (f(TMDS clk) * N) / (128 * f(s)) cap_i2c_read_data(pSiI9031, pSiI9031->BaseDevice + 8, 0x06, data, 3); n = data[2]; n = (n << 8) | data[1]; n = (n << 8) | data[0]; cap_i2c_read_data(pSiI9031, pSiI9031->BaseDevice + 8, 0x0C, data, 3); cts = data[2]; cts = (cts << 8) | data[1]; cts = (cts << 8) | data[0]; RMDBGLOG((ENABLE, "[SiI9031] N/CTS: %lu/%lu\n", n, cts)); if (pN) *pN = n; if (pCTS) *pCTS = cts; if (pApproxSamplingRate) { if (n && cts) { cap_i2c_read_dev(pSiI9031, pSiI9031->BaseDevice, 0x6F, &data[0]); // xpcnt // TODO HDMI 1.3: DeepColor TMDS clock != Pixel clock *pApproxSamplingRate = data[0] ? RM64mult32div32(28322000, n, data[0] * cts) : 0; } else { *pApproxSamplingRate = 0; } } return RM_OK;}/* set cable equalisation value to compensate for cable length, 0..15 */RMstatus cap_SiI9031_set_cable_comp( struct cap_SiI9031_instance *pSiI9031, RMuint32 cable_eq){ RMuint8 eq; RMDBGLOG((FUNCNAME, "%s\n",__func__)); // Sanity checks if (pSiI9031 == NULL) return RM_FATALINVALIDPOINTER; eq = cable_eq & 0x0F; eq = (eq << 4) | (0x0F - eq); cap_i2c_write_dev(pSiI9031, pSiI9031->BaseDevice, 0x81, eq); return RM_OK;}/* TRUE: upsample 4:2:2 to 4:4:4, FALSE: pass through 4:4:4 */RMstatus cap_SiI9031_set_422_to_444_upsampling( struct cap_SiI9031_instance *pSiI9031, RMbool upsampling){ RMuint8 reg; RMDBGLOG((FUNCNAME, "%s\n",__func__));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -