////////////////////////////////////////////////////////////////////////////// // // (C) Philips Semiconductors-CSU and Microsoft 1999 // All rights are reserved. Reproduction in whole or in part is prohibited // without the written consent of the copyright owner. // // Philips reserves the right to make changes without notice at any time. // Philips makes no warranty, expressed, implied or statutory, including but // not limited to any implied warranty of merchantibility or fitness for any // particular purpose, or that the use will not infringe any third party // patent, copyright or trademark. Philips must not be liable for any loss // or damage arising from its use. // // TUNER.CPP ////////////////////////////////////////////////////////////////////////////// #include "philtune.h" /* * CTuner() * Input : * Output: TRUE - if initialization data can be written to I2C * FALSE - if there is an I2C error * Description: CTuner Constructor. */ CTuner::CTuner(CI2CScript *p_I2CScript, BoardInfoType *p_BoardInfo, NTSTATUS *pStatus) { m_pI2CScript = p_I2CScript; m_ucTunerAddress = TUNER_I2C_ADDRESS; m_ulInput = 0L; // unknown input or the only one m_ulCurrentFrequency = 0L; // unknown tuning frequency m_ulMode = 0; m_ulPrevMode = 0; m_TunerID = TD1536; m_ulPreviousFrequency = 0L; //m_FrequencyParam.ulCurrentCFrequency = 0L; LONG lPLLOffset; BOOL bBusyStatus; //GetPLLOffsetBusyStatus(&lPLLOffset, &bBusyStatus); m_uiBoardID = 0; if(p_BoardInfo != NULL) { NTSTATUS status; status = SetCapabilities(p_BoardInfo); if(pStatus != NULL) *pStatus = status; } } /* * ~CTuner() * Input : * Output: * Description: CTuner Destructor. */ CTuner::~CTuner() { } /* * SetCapabilities() * Purpose : Sets the capabilities based upon the Tuner Id * * Inputs : UINT tuner : Tuner Id * * Outputs : returns TRUE, if there is a supported Tuner Id specified; * * Author : MM */ NTSTATUS CTuner::SetCapabilities(BoardInfoType *p_BoardInfo) { NTSTATUS nStatus = STATUS_SUCCESS; m_TunerID = (TunerTypes)(p_BoardInfo->uiTunerID); m_ucTunerAddress = p_BoardInfo->ucTunerAddress; m_uiBoardID = p_BoardInfo->uiBoardID; // Note: // If mode is KSPROPERTY_TUNER_MODE_ATSC, then the IF is // 43.75MHz, else it is 44MHz. But as the frequency // being passed (ulFrequency) is the Video Signal freq, // the calculation should take into consideration that the video signal // is 1.75 MHz from the centre of the band. This 1.75MHz should be // added to the actual IF hence for ATSC IF = 43.75 + 1.75 = 45.5MHz // and NTSC IF = 44 + 1.75 = 45.75MHz // Currently , supporting only TD1536, other tune support can be added later // as necessary switch(m_TunerID) { case TD1536: // Digital Tuner { // Check to determine if it is a single input or a dual input // tuner ULONG inputs = 1; GetNumberOfInputs(&inputs); int i = 0; if(p_BoardInfo->ulSupportedModes & KSPROPERTY_TUNER_MODE_TV) { // Set mode capabilities for TV mode m_ModeCaps[i].ModeCaps.ulMode = KSPROPERTY_TUNER_MODE_TV; m_ModeCaps[i].ModeCaps.ulNumberOfInputs = inputs; //2; m_ModeCaps[i].ModeCaps.ulMinFrequency = 55250000L; m_ModeCaps[i].ModeCaps.ulMaxFrequency = 801250000L; m_ModeCaps[i].ModeCaps.ulStrategy = KS_TUNER_STRATEGY_PLL; m_ModeCaps[i].ulIntermediateFrequency = 45750000L; m_ModeCaps[i].ModeCaps.ulStandardsSupported = KS_AnalogVideo_NTSC_M; m_ModeCaps[i].ulNumberOfStandards = 1; m_ModeCaps[i].ModeCaps.ulTuningGranularity = 62500L; m_ModeCaps[i].ModeCaps.ulSettlingTime = 150; // 150 ms i++; } if(p_BoardInfo->ulSupportedModes & KSPROPERTY_TUNER_MODE_ATSC) { // Set mode capabilities for ATSC mode m_ModeCaps[i].ModeCaps.ulMode = KSPROPERTY_TUNER_MODE_ATSC; m_ModeCaps[i].ModeCaps.ulNumberOfInputs = inputs; //2; m_ModeCaps[i].ModeCaps.ulMinFrequency = 55250000L; m_ModeCaps[i].ModeCaps.ulMaxFrequency = 801250000L; m_ModeCaps[i].ModeCaps.ulStrategy = KS_TUNER_STRATEGY_DRIVER_TUNES; if((m_uiBoardID == BOARD_CATALINA) || (m_uiBoardID == BOARD_CORFU)) m_ModeCaps[i].ulIntermediateFrequency = 45750000L; else m_ModeCaps[i].ulIntermediateFrequency = 45500000L; m_ModeCaps[i].ModeCaps.ulStandardsSupported = KS_AnalogVideo_NTSC_M; m_ModeCaps[i].ulNumberOfStandards = 0; m_ModeCaps[i].ModeCaps.ulTuningGranularity = 62500L; m_ModeCaps[i].ModeCaps.ulSettlingTime = 800; // 800ms i++; } m_ulSupportedModes = p_BoardInfo->ulSupportedModes; m_ulNumSupportedModes = p_BoardInfo->ulNumSupportedModes; m_ucTunerAddress = TUNER_I2C_ADDRESS; _DbgPrintF( DEBUGLVL_VERBOSE,("CDevice::Supported Modes = %x \n", m_ulSupportedModes)); } break; default: return STATUS_INVALID_PARAMETER; } SetMode(KSPROPERTY_TUNER_MODE_ATSC); m_ulVideoStandard = KS_AnalogVideo_NTSC_M; return nStatus; } /* * GetModeCapabilities() * Inputs: TunerModeCapsType *p_TunerModeCaps : pointer to * mode capability structure of the tuner * Outputs: Filled TunerModeCapsType structure * Returns: BOOL: returns TRUE, if the operation succeeds else FALSE * Description: Returns the mode capabilities of tuner for a particluar mode. */ NTSTATUS CTuner::GetModeCapabilities(TunerModeCapsType *p_TunerModeCaps) { ULONG ulOperationMode = p_TunerModeCaps->ulMode; _DbgPrintF( DEBUGLVL_VERBOSE,("CTuner::GetTunerModeCapbilities Mode = %x Mode in obj %x\n", ulOperationMode, m_ulMode )); // QF:This is a work-around, as the mode passed by the filter the 1st time // is not correct. Will have to be removed later. p_TunerModeCaps->ulMode = m_ulMode; ulOperationMode = m_ulMode; if (!(ulOperationMode & m_ulSupportedModes)) { // TRAP; return STATUS_INVALID_PARAMETER; } // There is support for TVTuner at this time only. // It will be enchanced later on to support FM Tuner as well. for(ULONG i = 0; i < m_ulNumSupportedModes; i++) { if(ulOperationMode == m_ModeCaps[m_ulModeCapIndex].ModeCaps.ulMode) break; } MemoryCopy(&p_TunerModeCaps->ulMode, &m_ModeCaps[m_ulModeCapIndex].ModeCaps, sizeof(TunerModeCapsType)); return STATUS_SUCCESS; } /* * SetMode() * Inputs: ULONG ulMode : an operation mode required to be set * Outputs: * Returns: UINT: 0 - if mode is not supported * 1 - if mode is same as previous mode * 2 - if new mode has been set * Description: Set TV mode */ NTSTATUS CTuner::SetMode(ULONG ulMode) { ULONG i; // Check if mod eis supported if(ulMode & m_ulSupportedModes) { m_ulPrevMode = m_ulMode; // Change mode only if it is different from the previous mode if(ulMode != m_ulMode) { // Check if the mode supported is part of the mode capability // structure array for the tuner. If it is , get the index into the // array for the given mode and change the mode. for(i = 0; i < m_ulNumSupportedModes; i++) { if(m_ModeCaps[i].ModeCaps.ulMode == ulMode) { m_ulModeCapIndex = i; break; } } if(i == m_ulNumSupportedModes) { _DbgPrintF( DEBUGLVL_ERROR,("CTuner::SetMode: Couldn't find mode in capability array\n")); return STATUS_INVALID_PARAMETER; } else m_ulMode = ulMode; } return STATUS_SUCCESS; } else { _DbgPrintF( DEBUGLVL_ERROR,("CTuner: Mode not supported : %x %x \n", ulMode, m_ulSupportedModes)); return STATUS_INVALID_PARAMETER; } } /* * GetMode() * Inputs: ULONG *p_ulMode : pointer to operation mode that has to be read * Outputs: operation mode * Returns: * Description: Get Mode (TV/ATSC) */ void CTuner::GetMode(ULONG *p_ulMode) { *p_ulMode = m_ulMode ; } /* * SetVideoStandard() * Inputs: ULONG ulStandard : a standard required to be set * Outputs: * Returns: NTSTATUS: STATUS_INVALID_PARAMETER - if standard is not supported * STATUS_SUCCESS - if operation succeeded * Description: Set the TV video standard requested. */ NTSTATUS CTuner::SetVideoStandard(ULONG ulStandard) { if(ulStandard & m_ModeCaps[m_ulModeCapIndex].ModeCaps.ulStandardsSupported) { if(ulStandard != m_ulVideoStandard) { m_ulVideoStandard = ulStandard; } return STATUS_SUCCESS; } return STATUS_INVALID_PARAMETER; } /* * GetVideoStandard() * Inputs: ULONG *p_ulStandard : pointer to standard required to be filled * Outputs: standard * Returns: * Description: Get the TV video standard requested. */ void CTuner::GetVideoStandard(ULONG *p_ulStandard) { *p_ulStandard = m_ulVideoStandard; } /* * GetPLLOffsetBusyStatus() * Purpose: Returns tuner Busy status and PLLOffset, if the tuner is not busy * The function reads the hardware in order to accomplish the task * The operation might be carried on either synchronously or asynchronously * Inputs : PLONG plPLLOffset : a pointer to write a PLLOffset value * PBOOL pbBusyStatus : a pointer to write a Busy status * * Outputs: BOOL : returns TRUE, if the operation succeded * Author : MM */ NTSTATUS CTuner::GetPLLOffsetBusyStatus(PLONG plPLLOffset, PBOOL pbBusyStatus) { UCHAR ucI2CValue = 0; NTSTATUS nResult = STATUS_SUCCESS; if( Read(&ucI2CValue, 1, 0) != WDMMINI_NOERROR) nResult = STATUS_ADAPTER_HARDWARE_ERROR; if (nResult == STATUS_SUCCESS) { // bit 6 - PLL locked indicator *pbBusyStatus = !((BOOL)(ucI2CValue & 0x40)); if (!(* pbBusyStatus)) { ucI2CValue &= 0x07; // only 3 LSBits are PLLOffset // let's map the result into MS defined values // from -2 to 2 *plPLLOffset = ucI2CValue - 2; } } // Read only busy bit for TD1536 as the tuner does not provide // PLL offset information. if (m_TunerID == TD1536) { *plPLLOffset = 0; // *pbBusyStatus = 0; // return TRUE; } return nResult; } /* * SetFrequency() * Purpose: Sets a new Tuner frequency * Inputs : ULONG ulFrequency : a frequency required to be set * * Outputs: BOOL : returns TRUE, if the operation succeded * Author : MM */ BOOL CTuner::SetFrequency(ULONG ulFrequency) { ASSERT(m_ModeCaps[m_ulModeCapIndex].ulIntermediateFrequency != 0L); // Change frequency if (!ChangeFrequency(ulFrequency)) return FALSE; m_ulCurrentFrequency = ulFrequency; if (m_ulPreviousFrequency != ulFrequency) { // Mini: Delay for 400ms to let the tuner settle to a tuned state and to let // the VSB acquire equalizer lock if (m_ulMode == KSPROPERTY_TUNER_MODE_ATSC) Delay(400000); } _DbgPrintF( DEBUGLVL_VERBOSE,("CTuner::SetTunerFrequency(): PrevFreq = %d CurrentFreq = %d\n", m_ulPreviousFrequency,m_ulCurrentFrequency)); m_ulPreviousFrequency = ulFrequency; return TRUE; } /* * GetFrequency() * Purpose: Gets the Tuner frequency * Inputs : ULONG *p_ulFrequency : a frequency required * * Outputs: * Author : MM */ void CTuner::GetFrequency(ULONG *p_ulFrequency) { *p_ulFrequency = m_ulCurrentFrequency; } /* * ChangeFrequency() * Input : frequency * Output: TRUE if able to to tune to the frequency * FALSE if unable to tune to the frequency * Description: Change the frequency of tuner to that specified */ BOOL CTuner::ChangeFrequency(ULONG ulFrequency) { ULONG ulFrequenceDivider; USHORT usControlCode; UCHAR ucI2CBuffer[6]; I2CPacket i2cPacket; BOOL bResult; ULONG IF = m_ModeCaps[m_ulModeCapIndex].ulIntermediateFrequency; // Set the video carrier frequency by controlling the programmable divider // N = (16 * (FreqRF + FreqIntermediate)) / 1000000 _DbgPrintF( DEBUGLVL_VERBOSE,("CTuner: ulFrequency = %x \n", ulFrequency)); ulFrequenceDivider = ulFrequency + IF; _DbgPrintF( DEBUGLVL_VERBOSE,("CTuner::ChangeFrequency: IF = %d\n", IF)); ulFrequenceDivider /= (1000000 / 16); // divide by 62,500 usControlCode = GetControlCode(ulFrequenceDivider); if(!usControlCode) return(FALSE); // _DbgPrintF( DEBUGLVL_VERBOSE,("PhilTune: ulFrequencyDivider before %x \n", ulFrequenceDivider)); // _DbgPrintF( DEBUGLVL_VERBOSE,("PhilTune: ulFrequencyDivider after %x \n", ulFrequenceDivider)); ucI2CBuffer[0] = 0xCE; ucI2CBuffer[1] = (UCHAR)usControlCode; ucI2CBuffer[2] = (UCHAR)(ulFrequenceDivider >> 8); ucI2CBuffer[3] = (UCHAR)ulFrequenceDivider; ucI2CBuffer[4] = (UCHAR)(usControlCode >> 8); ucI2CBuffer[5] = (UCHAR)usControlCode; _DbgPrintF( DEBUGLVL_TERSE,("\n CPhilipsWDMTuner:Tuner Control Code = %x %x %x %x \n", ucI2CBuffer[0], ucI2CBuffer[1], ucI2CBuffer[2], ucI2CBuffer[3])); /*i2cPacket.uchChipAddress = m_uchTunerI2CAddress; i2cPacket.cbReadCount = 0; i2cPacket.cbWriteCount = 4; i2cPacket.puchReadBuffer = NULL; i2cPacket.puchWriteBuffer = auchI2CBuffer; i2cPacket.usFlags = 0; bResult = m_pI2CScript->PerformI2CPacketOperation(&i2cPacket); return bResult; */ if(Write(ucI2CBuffer, sizeof(ucI2CBuffer), 0) == WDMMINI_NOERROR) return TRUE; else return FALSE; } BOOL CTuner::TweakChannel(LONG lTweak, int iTweakReference) { // Should change the routine later to support tweak reference // if tweak reference is TUNER_ABSOLUTE_TWEAK, then tweaking is about the centre // frequency else if tweak reference is TUNER_RELATIVE_TWEAK, then tweaking is about // the current frequency LONG lTweakFrq = (lTweak * 62500) + m_ulCurrentFrequency; if (lTweakFrq > 0) if (!ChangeFrequency((ULONG)lTweakFrq)) return FALSE; else return FALSE; m_ulCurrentFrequency = (ULONG)lTweakFrq; return TRUE; } /* * GetNumberOfInputs() * Input : pointer to ULONG variable which will be filled with number of inputs * Output: TRUE - if the number of inputs can be determined * FALSE - if there is an I2C error & number of inputs can't be determined * Description: Determine the number of tuner inputs */ BOOL CTuner::GetNumberOfInputs(ULONG *p_ulInputs) { UCHAR ucMode = 0; _DbgPrintF( DEBUGLVL_VERBOSE,("CTuner::GetNumberOfInputs: Inside\n")); if(m_uiBoardID == BOARD_CONEY) { if(!m_pI2CScript->ReadSeq(CONEY_I2C_PARALLEL_PORT, &ucMode, 1)) { _DbgPrintF( DEBUGLVL_ERROR,("CTuner::GetNumberOfInputs: Error\n")); return(FALSE); } // If the mode bit 0 = 1,then its a dual input tuner , else its a single input tuner if ((ucMode & 0x1) == 0) *p_ulInputs = 1; else *p_ulInputs = 2; } else //if(m_uiBoardID == BOARD_CATALINA) *p_ulInputs = 1; // else // { // *p_ulInputs = 1; // _DbgPrintF( DEBUGLVL_ERROR,("CTuner::GetNumberOfInputs:Invalid Board ID\n")); // } m_uiNumInputs = *p_ulInputs; _DbgPrintF( DEBUGLVL_VERBOSE,("CTuner::GetNumberOfInputs: Number of input pins = %d Mode = %x\n", *p_ulInputs, ucMode)); return(TRUE); } /* * GetInput() * Purpose: Gets the current tuner inputs as an active one * Inputs : ULONG nInput : input number required to be set as an active * (begins from 0) * * Outputs: BOOL : returns TRUE, if the operation succeded * Author : MM */ BOOL CTuner::GetInput(ULONG *p_ulInput) { *p_ulInput = m_ulInput; return(TRUE); } /* * SetInput() * Purpose: Sets one of the possible Tuner inputs as an active one * Inputs : ULONG nInput : input number required to be set as an active * (begins from 0) * * Returns: UINT: 0 - if tuner input is out of range * 1 - if tuner input is same as previous tuner input * 2 - if new tuner input has been set * Author : MM */ UINT CTuner::SetInput(ULONG ulInput) { if(ulInput < m_ModeCaps[m_ulModeCapIndex].ModeCaps.ulNumberOfInputs) { if(ulInput != m_ulInput) m_ulInput = ulInput; else return 1; return 2; } else return 0; } /* * GetControlCode() * Purpose: Determines the Tuner control code to be send to tuner with a new frequency value * * Inputs : ULONG ulFrequencyDivider : new frequency divider * * Outputs: USHORT : value, the tuner should be programmed, when the new frequency is set * id the is no valid uiTunerId is passed as paramter, 0 is returned * Author : MM */ USHORT CTuner::GetControlCode(ULONG ulFrequencyDivider) { USHORT usLowBandFrequencyHigh, usMiddleBandFrequencyHigh; USHORT usLowBandControl, usMiddleBandControl, usHighBandControl; USHORT usControlCode = 0; usLowBandFrequencyHigh = kUpperLowBand; usMiddleBandFrequencyHigh = kUpperMidBand; usLowBandControl = kLowBand; usMiddleBandControl = kMidBand; usHighBandControl = kHighBand; switch(m_TunerID) { case TD1536: { if (m_ulMode != KSPROPERTY_TUNER_MODE_ATSC) { usLowBandControl = kLowBand_1536_NTSC_A; usMiddleBandControl = kMidBand_1536_NTSC_A; usHighBandControl = kHighBand_1536_NTSC_A; if(m_uiBoardID == BOARD_CORONADO) { usLowBandControl &= 0xffbf; usMiddleBandControl &= 0xffbf; usHighBandControl &= 0xffbf; } } else { usLowBandControl = kLowBand_1536_NTSC_D; usMiddleBandControl = kMidBand_1536_NTSC_D; usHighBandControl = kHighBand_1536_NTSC_D; if(m_uiBoardID == BOARD_CORONADO) { usLowBandControl |= 0x40; usMiddleBandControl |= 0x40; usHighBandControl |= 0x40; } } // Based on the tuner input modify control word // Test ULONG ulInp = m_ulInput; //1 if (ulInp == 1) { usLowBandControl |= 0x1; usMiddleBandControl |= 0x1; usHighBandControl |= 0x1; } else { usLowBandControl &= 0xfffe; usMiddleBandControl &= 0xfffe; usHighBandControl &= 0xfffe; } _DbgPrintF( DEBUGLVL_VERBOSE,("CPhilipsWDMTuner::GetControlCode(): LBand = %x, MBand = %x, HBand = %x\n", usLowBandControl, usMiddleBandControl, usHighBandControl)); } break; default : return(usControlCode); } if(ulFrequencyDivider <= (ULONG)usLowBandFrequencyHigh) usControlCode = usLowBandControl; else { if(ulFrequencyDivider <= (ULONG)usMiddleBandFrequencyHigh) usControlCode = usMiddleBandControl; else usControlCode = usHighBandControl; } return(usControlCode); } /* * Write() * Input:UCHAR *p_ucBuffer - buffer to be written * int uiNumReg - Number of registers to be written * UINT uiStartAddr - start address * Output : UINT - Error code * Description: Write data to chip */ UINT CTuner::Write(UCHAR *p_ucBuffer, UINT uiNumReg, UINT uiStartAddr) { UINT uiResult = WDMMINI_NOERROR; // The present versions of the chip do not support sub-addressing, hence // uiStartAddr is not used. // write to chip //$REVIEW - Should change function decl to make uiNumReg be USHORT - TCP if(!m_pI2CScript->WriteSeq(m_ucTunerAddress, p_ucBuffer, (USHORT) uiNumReg)) uiResult = WDMMINI_HARDWAREFAILURE; return uiResult; } /* * Read() * Input:UCHAR *p_ucBuffer - buffer to be filled * int uiNumReg - Number of registers to be read * UINT uiStartAddr - start address * Output : UINT - Error code * Description: Read data from chip */ UINT CTuner::Read(UCHAR *p_ucBuffer, UINT uiNumReg, UINT uiStartAddr) { UINT uiResult = WDMMINI_NOERROR; // The present versions of the chip do not support sub-addressing, hence // uiStartAddr is not used. // write to chip //$REVIEW - Should change function decl to make uiNumReg be USHORT - TCP if(!m_pI2CScript->ReadSeq(m_ucTunerAddress, p_ucBuffer, (USHORT) uiNumReg)) uiResult = WDMMINI_HARDWAREFAILURE; return uiResult; } #if 0 /* * operator new * Purpose: CTuner class overrides operator new. * * Inputs : UINT uiSize : size of the object to be placed * * Outputs: PVOID : pointer of the CTuner class object * Author : MM */ PVOID CTuner::operator new(UINT uiSize) { if (uiSize != sizeof(CTuner)) { _DbgPrintF( DEBUGLVL_ERROR,("CTuner: operator new() fails\n")); return(NULL); } return (AllocateFixedMemory(uiSize)); } /* * operator delete * Purpose: CTuner class overrides operator delete * * Inputs : PVOID p_Buffer : pointer to object being deleted * * Outputs: * Author : MM */ void CTuner::operator delete(PVOID p_Object) { if(p_Object != NULL) FreeFixedMemory(p_Object); _DbgPrintF( DEBUGLVL_VERBOSE,("CTuner: operator delete() succeeds\n")); } /* * SetFrequencyParam() * Purpose: Sets the frequency parameters for the tuner * Inputs : TunerFrequencyType *p_Frequency : a frequency required to be set * * Returns: UINT: 0 - if frequency is out of range or frequency setting fails * 1 - if frequency is same as previous frequency * 2 - if new frequency has been set * Author : MM */ UINT CTuner::SetFrequencyParam(TunerFrequencyType *p_Frequency) { ULONG ulFrequency = p_Frequency->ulCurrentCFrequency; if((ulFrequency < m_ModeCaps[m_ulModeCapIndex].ModeCaps.ulMinFrequency) || (ulFrequency > m_ModeCaps[m_ulModeCapIndex].ModeCaps.ulMaxFrequency)) return 0; // If the tuning frequency has changed or the tuner mode has changed , // then change the tuner frequency if((ulFrequency != m_ulCurrentFrequency) || (m_ulPrevMode != m_ulMode)) { // Set the tuner frequency if(!SetFrequency(ulFrequency)) return 0; // Update the CTuner's frequency parameters MemoryCopy(&m_FrequencyParam, p_Frequency, sizeof(TunerFrequencyType)); return 2; } return 1; } /* * GetFrequencyParam() * Purpose: Gets the frequency parameters for the tuner * Inputs : TunerFrequencyType *p_Frequency : a frequency required to be filled * Output: * Author : MM */ void CTuner::GetFrequencyParam(TunerFrequencyType *p_Frequency) { MemoryCopy(p_Frequency, &m_FrequencyParam, sizeof(TunerFrequencyType)); return; } #endif