/*************************************************************************** * Copyright (c) 2003-2006, Broadcom Corporation * All Rights Reserved * Confidential Property of Broadcom Corporation * * THIS SOFTWARE MAY ONLY BE USED SUBJECT TO AN EXECUTED SOFTWARE LICENSE * AGREEMENT BETWEEN THE USER AND BROADCOM. YOU HAVE NO RIGHT TO USE OR * EXPLOIT THIS MATERIAL EXCEPT SUBJECT TO THE TERMS OF SUCH AN AGREEMENT. * * $brcm_Workfile: $ * $brcm_Revision: $ * $brcm_Date: $ * * Module Description: * * Revision History: * * $brcm_Log: $ * ***************************************************************************/ #include "bsettop_tuner.h" #include "bads.h" #include "bads_3x7x.h" #include "bads_api.h" #include "btnr.h" #include "btnr_3x7x.h" #include "gist.h" #include "bkni_multi.h" #include "bos_task_priorities.h" #include "bapp_util.h" BDBG_MODULE(btuner); /* Register software module with debug interface */ #define DS_TASK_STACK 0x400 #define TIMEOUT_INC_MS 50 #define TUNE_TIMEOUT_MS (3*350) #define SCAN_TIMEOUT_MS (3*500) struct btuner { b_task_params task_params; b_task_t task_h; unsigned int task_stack[DS_TASK_STACK]; BKNI_EventHandle isrEvent; BKNI_EventHandle lockEvent; BKNI_EventHandle bbsEvent; btuner_params last_params; BI2C_ChannelHandle i2cChannelHandle; BREG_I2C_Handle i2cRegHandle; BTNR_Handle tnrHandle; BADS_Handle adsHandle; BADS_ChannelHandle adsChannelHandle; unsigned int freq_hz; unsigned int timeout_ms; b_mutex_t mutex; }; /* Summary: btuner_ads_tuner_cb is called by ADS. */ void btuner_ads_tuner_cb(void *param) { BTNR_3x7x_RfStatus_t RfCallbackStatus; BADS_P_AdsCallbackData_t *pCallback = (BADS_P_AdsCallbackData_t *)param; btuner_t tuner = (btuner_t)pCallback->hTunerChn; BDBG_MSG(("%s: %d\n", __FUNCTION__,__LINE__)); if (BADS_CallbackMode_eSetMode == pCallback->Mode) { /* TODO: call the tuner with pCallback->Freq_Offset */ BTNR_3x7x_Set_RF_Offset( tuner->tnrHandle, pCallback->Freq_Offset, pCallback->Symbol_Rate); } else { /* magic numbers from hardware team */ BTNR_3x7x_Get_RF_Status(tuner->tnrHandle, &RfCallbackStatus); pCallback->RF_Freq = RfCallbackStatus.RF_Freq; pCallback->Total_Mix_After_ADC = RfCallbackStatus.Total_Mix_After_ADC; pCallback->PreADC_Gain_x256db = RfCallbackStatus.PreADC_Gain_x256db; pCallback->PostADC_Gain_x256db = RfCallbackStatus.PostADC_Gain_x256db; pCallback->External_Gain_x256db = RfCallbackStatus.External_Gain_x256db; pCallback->Freq_Offset = RfCallbackStatus.RF_Offset; pCallback->Symbol_Rate = RfCallbackStatus.Symbol_Rate; } } /* Summary: btuner_ads_lock_cb is called by ADS to provide notification of lock/unlock */ void btuner_ads_lock_cb(void *param) { BADS_LockStatus lockStatus; btuner_t tuner = (btuner_t)param; BDBG_ASSERT(tuner); BDBG_ASSERT(tuner->adsChannelHandle); if (BADS_GetLockStatus(tuner->adsChannelHandle,&lockStatus) == BERR_SUCCESS) { BDBG_MSG(("%s: %s", __func__, (BADS_LockStatus_eLocked == lockStatus) ? "Locked" : (BADS_LockStatus_eNoSignal == lockStatus) ? "No Signal" : "Not Locked")); BKNI_SetEvent(tuner->lockEvent); } } /* Summary: Tune the actual tuner. */ static void btuner_tune_tuner(btuner_t tuner) { BTNR_PowerSaverSettings pwrSettings; BTNR_Settings tnrSettings; /* TODO: we should be able to call BTNR_SetSettings after BTNR_SetPowerSaver. verify */ pwrSettings.enable = false; BTNR_SetPowerSaver(tuner->tnrHandle, &pwrSettings); BTNR_GetSettings(tuner->tnrHandle, &tnrSettings); tnrSettings.tnrApplication = BTNR_TunerApplication_eCable; tnrSettings.bandwidth = 6000000; tnrSettings.rfInputMode = BTNR_RfInputMode_eInternalLna; BTNR_SetSettings(tuner->tnrHandle, &tnrSettings); BTNR_SetTunerRfFreq(tuner->tnrHandle, tuner->freq_hz, BTNR_TunerMode_eDigital); BADS_DisablePowerSaver(tuner->adsChannelHandle); } /*************************************************************************** Summary: Acquisition processing thread ***************************************************************************/ static void ds_task(void *arg) { btuner_t p_tune = (btuner_t)arg; BDBG_ASSERT(p_tune); BDBG_MSG(("ds_task")); BERR_Code rc; while (1) { if (0 == p_tune->freq_hz) { /* if no frequency is given, skip it */ bos_sleep(100); continue; } rc = BKNI_WaitForEvent(p_tune->bbsEvent,10); if (BERR_SUCCESS == rc) { BDBG_MSG(("%s BBS Event",__FUNCTION__)); /* only return if user don't issue tune request */ if (bos_acquire_mutex(&(p_tune->mutex), BOS_PEND_FOREVER) == b_ok) { BTNR_3x7x_ProcessInterruptEvent(p_tune->tnrHandle); bos_release_mutex(&(p_tune->mutex)); } } rc = BKNI_WaitForEvent(p_tune->isrEvent,10); if (BERR_TIMEOUT != rc) { BDBG_WRN(("%s ISR Event",__FUNCTION__)); if (bos_acquire_mutex(&(p_tune->mutex), BOS_PEND_FOREVER) == b_ok) { BADS_ProcessInterruptEvent(p_tune->adsHandle); bos_release_mutex(&(p_tune->mutex)); } } bos_sleep(40); } } /* Summary: To achieve best performance the fastAcquire needs to be true for normal tune/acquire but false for channel scanning. Also disabling and re-enabling the channel prevents redundent acquires. */ void btuner_ads_open( btuner_t p_tune, /* - handle used to identify a particular tuner */ bool autoAcquire, /* enable auto reacquisiton */ bool fastAcquire /* enable "fast acquisition */ ) { BADS_ChannelSettings adsChannelSettings; BDBG_ASSERT(p_tune); BDBG_ASSERT(p_tune->adsHandle); BADS_GetChannelDefaultSettings(p_tune->adsHandle, 0, &adsChannelSettings); BADS_OpenChannel(p_tune->adsHandle, &p_tune->adsChannelHandle, 0, &adsChannelSettings); BDBG_ASSERT(p_tune->adsChannelHandle); BADS_InstallCallback(p_tune->adsChannelHandle, BADS_Callback_eLockChange, (BADS_CallbackFunc)btuner_ads_lock_cb, p_tune); BADS_InstallCallback(p_tune->adsChannelHandle, BADS_Callback_eTuner, (BADS_CallbackFunc)btuner_ads_tuner_cb, p_tune); } /* Summary: To achieve best performance the fastAcquire needs to be true for normal tune/acquire but false for channel scanning. Also disabling and re-enabling the channel prevents redundent acquires. */ void btuner_ads_close( btuner_t p_tune /* - handle used to identify a particular tuner */ ) { BDBG_ASSERT(p_tune); if (p_tune->adsChannelHandle) { BADS_CloseChannel(p_tune->adsChannelHandle); p_tune->adsChannelHandle = NULL; } } /* Summary: Open a tuner. Description: On most platforms, the tuner_id is simply an index of the tuners. On platforms with more frontend options (e.g. 97038), please see bsettop_ids.txt for documentation on special numbers. */ btuner_t btuner_open( bobject_t tuner_id /* - handle used to identify a particular tuner */ ) { btuner_t p_tune = NULL; p_tune = (btuner_t)BKNI_Malloc(sizeof(struct btuner)); bresult rc; if (!p_tune) return NULL; BKNI_Memset(p_tune,0,sizeof(struct btuner)); p_tune->task_params.name="ds_task"; p_tune->task_params.priority = VSB_ACQ_PRIORITY; p_tune->task_params.stack_size = DS_TASK_STACK; p_tune->task_params.stack = p_tune->task_stack; #if 0 BDBG_SetModuleLevel("bads_acquire",BDBG_eMsg); BDBG_SetModuleLevel("bads_3x7x",BDBG_eMsg); BDBG_SetModuleLevel("bads_utils",BDBG_eMsg); BDBG_SetModuleLevel("bads_api",BDBG_eMsg); BDBG_SetModuleLevel("bads_status",BDBG_eMsg); BDBG_SetModuleLevel("btnr_tune",BDBG_eMsg); BDBG_SetModuleLevel("btnr_init",BDBG_eMsg); BDBG_SetModuleLevel("btnr_3x7x_priv",BDBG_eMsg); #endif BTNR_3x7x_Settings tnrSettings; BADS_Settings adsSettings; BKNI_CreateEvent(&(p_tune->lockEvent)); BDBG_ASSERT(p_tune->lockEvent); tnrSettings.i2cAddr = 0; tnrSettings.hTmr = GetTMR(); tnrSettings.hHeap = GetHEAP(); BTNR_3x7x_Open( &p_tune->tnrHandle, &tnrSettings, GetREG()); BDBG_ASSERT(p_tune->tnrHandle); BTNR_3x7x_Set_RF_LoopThrough(p_tune->tnrHandle, false); /* Disable loop through for DTA low power requirments */ BTNR_3x7x_GetInterruptEventHandle(p_tune->tnrHandle, &p_tune->bbsEvent); BDBG_ASSERT(p_tune->bbsEvent); BADS_3x7x_GetDefaultSettings(&adsSettings,GetCHP()); adsSettings.hTmr = GetTMR(); adsSettings.hHeap = GetHEAP(); BADS_Open( &p_tune->adsHandle, GetCHP(), GetREG(), GetINT(), &adsSettings ); BDBG_ASSERT(p_tune->adsHandle); BADS_Init(p_tune->adsHandle); /* RLQ, _Get_DPM function will call callback unconditionally without checking the pointer, so have to install a dummy callback */ /* per Tak's request to power down dsisy buffer */ BADS_SetDaisyChain(p_tune->adsHandle, false); /* Get events and register callbacks */ BADS_GetInterruptEventHandle(p_tune->adsHandle, &p_tune->isrEvent); BDBG_ASSERT(p_tune->isrEvent); rc = bos_create_mutex(&p_tune->mutex); BDBG_ASSERT(rc == b_ok); btuner_ads_open(p_tune,false, false /*(params->qamMode != eQAM_Scan)*/); bos_start_task(&(p_tune->task_h),&(p_tune->task_params),ds_task,p_tune); return p_tune; } /* Summary: Close a tuner. */ void btuner_close( btuner_t tuner /* - handle returned by btuner_open */ ) { BDBG_ASSERT(tuner); BDBG_ASSERT(tuner->i2cChannelHandle); BDBG_ASSERT(tuner->i2cRegHandle); BDBG_ASSERT(tuner->tnrHandle); BDBG_ASSERT(tuner->adsHandle); BDBG_ASSERT(tuner->adsChannelHandle); btuner_ads_close(tuner); BADS_Close(tuner->adsHandle); BTNR_Close(tuner->tnrHandle); BI2C_CloseI2cRegHandle(tuner->i2cRegHandle); BI2C_CloseChannel(tuner->i2cChannelHandle); bos_delete_mutex(&tuner->mutex); BKNI_DestroyEvent(tuner->lockEvent); BKNI_Free(tuner); } /* Summary: Tune and acquire a VSB downstream band */ static const unsigned J83A_SYMBOLRATE_UPPER = 7200000; static const unsigned J83A_SYMBOLRATE_LOWER = 4000000; static const unsigned J83A_SYMBOLRATE_DEFAULT = 6952000; static const unsigned J83B_SYMBOLRATE_QAM64 = 5056941; static const unsigned J83B_SYMBOLRATE_QAM256 = 5360537; bband_t btuner_tune( btuner_t tuner, unsigned freq, /* - RF center frequency in Hz */ btuner_params *params /* - parameters needed to tune and acquire */ ) { BADS_InbandParam inbandParams; BERR_Code err; int timeout_cnt,lock_status; BADS_Status status; BADS_ModulationType modType; BADS_InvertSpectrum invert_spectrum = BADS_InvertSpectrum_eNoInverted; BADS_LockStatus lockStatus; bband_t result = -1; BDBG_MSG(("%s:%d Freq = %d\n",__FUNCTION__,__LINE__,freq)); if (bos_acquire_mutex(&(tuner->mutex),BKNI_INFINITE) != b_ok) { BDBG_ERR(("bos_acquire_mutex failed")); return result; } tuner->last_params = *params; tuner->freq_hz = freq; btuner_tune_tuner(tuner); BDBG_MSG(("%s:%d Freq = %d\n",__FUNCTION__,__LINE__,freq)); BKNI_Memset(&inbandParams,0,sizeof(inbandParams)); inbandParams.spectrum = BADS_SpectrumMode_eAuto; inbandParams.autoAcquire = false; timeout_cnt = TUNE_TIMEOUT_MS; if (params->qamMode == eQAM_Scan) { timeout_cnt = SCAN_TIMEOUT_MS; } else if (params->spectrum) { invert_spectrum = BADS_InvertSpectrum_eInverted; } /* start with QAM256 first if auto */ if (params->qamMode == eQAM_64) modType = BADS_ModulationType_eAnnexBQam64; else modType = BADS_ModulationType_eAnnexBQam256; inbandParams.spectrum = BADS_SpectrumMode_eAuto; inbandParams.enableDpm = BADS_DpmMode_Disabled; inbandParams.invertSpectrum = invert_spectrum; inbandParams.modType = modType; inbandParams.acquisitionType = BADS_AcquireType_eAuto; inbandParams.autoAcquire = true; BDBG_MSG(("%s Freq = %d\n",__FUNCTION__,freq)); BDBG_MSG(("%s inbandParams.modType = %d\n",__FUNCTION__,inbandParams.modType)); BDBG_MSG(("%s inbandParams.symbolRate = %d\n",__FUNCTION__,inbandParams.symbolRate)); BDBG_MSG(("%s inbandParams.invertSpectrum = %d\n",__FUNCTION__,inbandParams.invertSpectrum)); BDBG_MSG(("%s inbandParams.spectrum = %d\n",__FUNCTION__,inbandParams.spectrum)); BDBG_MSG(("%s inbandParams.enableDpm = %d\n",__FUNCTION__,inbandParams.enableDpm)); BDBG_MSG(("%s inbandParams.autoAcquire = %d\n",__FUNCTION__,inbandParams.autoAcquire)); err = BADS_Acquire(tuner->adsChannelHandle,&inbandParams); if (err != BERR_SUCCESS) { BDBG_ERR(("BADS_Acquire(%d,%d,%d) error %d\n",freq, inbandParams.modType,inbandParams.symbolRate, err)); bos_release_mutex(&(tuner->mutex)); return result; } lock_status = false; bos_release_mutex(&(tuner->mutex)); if ((params->qamMode == eQAM_Scan) && (lock_status == 2 /* BADS_Timing_eUnLocked */)) { BDBG_MSG(("BADS_Timing_eUnLocked - quick non-signal indication")); return result; } if (BKNI_WaitForEvent(tuner->lockEvent,timeout_cnt) != BERR_SUCCESS) { BDBG_MSG(("BKNI_WaitForEvent failed in %s",__FUNCTION__)); } if (bos_acquire_mutex(&(tuner->mutex),BKNI_INFINITE) != b_ok) { BDBG_ERR(("bos_acquire_mutex failed")); return result; } if ((err = BADS_GetLockStatus(tuner->adsChannelHandle,&lockStatus)) == BERR_SUCCESS) { BDBG_MSG(("%s: %s\n", __func__, (BADS_LockStatus_eLocked == lockStatus) ? "Locked" : (BADS_LockStatus_eNoSignal == lockStatus) ? "No Signal" : "Not Locked")); if (BADS_LockStatus_eLocked == lockStatus) { result = 0; params->qamMode = (status.modType == BADS_ModulationType_eAnnexBQam256) ? eQAM_256 : eQAM_64; params->spectrum = status.isSpectrumInverted; } } bos_release_mutex(&(tuner->mutex)); return result; } bresult btuner_reset_status(btuner_t tuner) { if (!tuner->freq_hz) return BERR_INVALID_PARAMETER; return BADS_ResetStatus(tuner->adsChannelHandle); } /* Summary: Get the status of DS receiver */ bresult btuner_get_status( btuner_t tuner, btuner_status *status /* - [out] Current status of the SDS demod */ ) { BERR_Code err; BADS_Status ads_status; BKNI_Memset(status,0,sizeof(*status)); if (!tuner->freq_hz) return BERR_INVALID_PARAMETER; err = BADS_GetStatus(tuner->adsChannelHandle,&ads_status); status->freq = tuner->freq_hz; status->lock = (ads_status.isFecLock && ads_status.isQamLock); status->fecLock = ads_status.isFecLock; status->qamLock = ads_status.isQamLock; switch (ads_status.modType) { case BADS_ModulationType_eAnnexAQam16: status->mode = eQAM_16; status->fecMode = eFEC_ANNEX_A; break; case BADS_ModulationType_eAnnexAQam32: status->mode = eQAM_32; status->fecMode = eFEC_ANNEX_A; break; case BADS_ModulationType_eAnnexAQam64: status->mode = eQAM_64; status->fecMode = eFEC_ANNEX_A; break; case BADS_ModulationType_eAnnexAQam128: status->mode = eQAM_128; status->fecMode = eFEC_ANNEX_A; break; case BADS_ModulationType_eAnnexAQam256: status->mode = eQAM_256; status->fecMode = eFEC_ANNEX_A; break; case BADS_ModulationType_eAnnexAQam512: status->mode = eQAM_512; status->fecMode = eFEC_ANNEX_A; break; case BADS_ModulationType_eAnnexAQam1024: status->mode = eQAM_1024; status->fecMode = eFEC_ANNEX_A; break; case BADS_ModulationType_eAnnexAQam2048: status->mode = eQAM_2048; status->fecMode = eFEC_ANNEX_A; break; case BADS_ModulationType_eAnnexAQam4096: status->mode = eQAM_4096; status->fecMode = eFEC_ANNEX_A; break; case BADS_ModulationType_eAnnexBQam16: status->mode = eQAM_16; status->fecMode = eFEC_ANNEX_B; break; case BADS_ModulationType_eAnnexBQam32: status->mode = eQAM_32; status->fecMode = eFEC_ANNEX_B; break; case BADS_ModulationType_eAnnexBQam64: status->mode = eQAM_64; status->fecMode = eFEC_ANNEX_B; break; case BADS_ModulationType_eAnnexBQam128: status->mode = eQAM_128; status->fecMode = eFEC_ANNEX_B; break; case BADS_ModulationType_eAnnexBQam256: status->mode = eQAM_256; status->fecMode = eFEC_ANNEX_B; break; case BADS_ModulationType_eAnnexBQam512: status->mode = eQAM_512; status->fecMode = eFEC_ANNEX_B; break; case BADS_ModulationType_eAnnexBQam1024: status->mode = eQAM_1024; status->fecMode = eFEC_ANNEX_B; break; case BADS_ModulationType_eAnnexBQam2048: status->mode = eQAM_2048; status->fecMode = eFEC_ANNEX_B; break; case BADS_ModulationType_eAnnexBQam4096: status->mode = eQAM_4096; status->fecMode = eFEC_ANNEX_B; break; default: return BERR_INVALID_PARAMETER; } #ifndef ACB612 if (status->get_power) #endif { #if (BCHP_CHIP==7550) BTNR_7550_TunerStatus TStatus; BTNR_7550_EnableDPM(tuner->tnrHandle); err = BTNR_7550_GetTunerStatus(tuner->tnrHandle, &TStatus); if (!err) { status->power = TStatus.dpmLvel >> 8; /* / 256 */ } else status->power = 0; #endif } //status->power = ads_status.dsChannelPower; status->snr = ads_status.snrEstimate; /* divide 256 per Charlie's advice */ status->snr >>= 8; status->goodRsBlockCount = ads_status.goodRsBlockCount; status->berRawCount = ads_status.berRawCount; status->agcIntLevel = ads_status.agcIntLevel; status->agcExtLevel = ads_status.agcExtLevel; status->carrierFreqOffset = ads_status.carrierFreqOffset; status->carrierPhaseOffset = ads_status.carrierPhaseOffset; status->rxSymbolRate = ads_status.rxSymbolRate; status->equalizerGain = ads_status.equalizerGain; status->postRsBER = ads_status.postRsBER; status->elapsedTimeSec = ads_status.elapsedTimeSec; status->isSpectrumInverted = ads_status.isSpectrumInverted; status->correctedCount = ads_status.correctedCount; status->uncorrectedCount = ads_status.uncorrectedCount; //status->reacquireCount = ads_status.reacquireCount; //status->resyncCount = ads_status.resyncCount; return BERR_SUCCESS; } /* Summary: Required to initialize VSB parameters to defaults */ void btuner_params_init( btuner_params *ds, /* - [out] */ btuner_t tuner /* - required for possible resource-dependent defaults */ ) { BKNI_Memset(ds,0,sizeof(btuner_params)); } #ifdef CONFIG_TUNER_TEST #include "bsettop.h" #include "ministd.h" static unsigned char ibuffer[0x100]; static unsigned int input_number(void) { unsigned number; gets(ibuffer); if ('0' ==ibuffer[0]) { switch (ibuffer[1]) { case 'x': sscanf(&ibuffer[2], "%x", &number); break; case 'X': sscanf(&ibuffer[2], "%x", &number); break; default: sscanf(ibuffer, "%d", &number); break; } } else { sscanf(ibuffer, "%d", &number); } printf(" %d %x", number, number); return number; } void bapp_tune_test(void) { btuner_t p_tune; char input; btuner_params params; unsigned int freq,qam_mode,symbol_rate,fec_mode; bool locked = false; bsettop_init(0); p_tune = btuner_open(B_ID(0)); BDBG_ASSERT(p_tune); start: printf("Enter frequency in Hz: "); freq = input_number(); printf("\n"); printf("FEC Mode(1-Annex B, 2-Annex A): "); fec_mode = input_number(); printf("\n"); if (fec_mode == 2) { printf("Enter symbol rate: "); symbol_rate = input_number(); printf("\n"); } printf("QAM Mode(1-QAM64, 2-QAM256): "); qam_mode = input_number(); printf("\n"); { btuner_params_init(¶ms,p_tune); if (qam_mode == 1) { params.qamMode = eQAM_64; printf("Tune QAM64\n"); } else if (qam_mode == 2) { params.qamMode = eQAM_256; printf("Tune QAM256\n"); } else { params.qamMode = eQAM_Scan; printf("Tune Scan\n"); } if (fec_mode == 2) { params.fecMode = eFEC_ANNEX_A; printf("Tune Annex A\n"); params.symbol_rate = symbol_rate; printf("Tune symbol rate = %d\n",symbol_rate); } else { params.fecMode = eFEC_ANNEX_B; printf("Tune Annex B\n"); } params.wait_for_lock = 1; if (btuner_tune(p_tune,freq,¶ms) < 0) { BDBG_ERR(("Tune to %dHz failed\n",freq)); } else { BDBG_ERR(("Tune to %dHz success\n",freq)); } } printf("Tune again? [y/n]: "); input = getchar(); printf("\n"); if (('y' == input)||('Y' == input)) { locked = false; goto start; } } #endif /* * Summary: * Get an arry of soft decision for a constellation * Description: * I and Q range from 32767 to -32768 */ #define TOTAL_ADS_SOFTDECISIONS 30 bresult btuner_get_softdecisions( btuner_t tuner, btuner_softdecision_t *pdec, /* - [out] array of soft decisions */ size_t length /* number of soft decisions to get */ ) { int i,j; bresult rc; int16_t nbr; int16_t d_i[TOTAL_ADS_SOFTDECISIONS], d_q[TOTAL_ADS_SOFTDECISIONS]; BDBG_ASSERT(tuner); BDBG_ASSERT(tuner->adsChannelHandle); for (i=0; iadsChannelHandle,(int16_t)TOTAL_ADS_SOFTDECISIONS, d_i, d_q, &nbr); if (rc) return BERR_INVALID_PARAMETER; for (j=0; ((j