| /* |
| Copyright (c), 2004-2005,2007-2010 Trident Microsystems, Inc. |
| All rights reserved. |
| |
| Redistribution and use in source and binary forms, with or without |
| modification, are permitted provided that the following conditions are met: |
| |
| * Redistributions of source code must retain the above copyright notice, |
| this list of conditions and the following disclaimer. |
| * Redistributions in binary form must reproduce the above copyright notice, |
| this list of conditions and the following disclaimer in the documentation |
| and/or other materials provided with the distribution. |
| * Neither the name of Trident Microsystems nor Hauppauge Computer Works |
| nor the names of its contributors may be used to endorse or promote |
| products derived from this software without specific prior written |
| permission. |
| |
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
| LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| POSSIBILITY OF SUCH DAMAGE. |
| |
| DRXJ specific implementation of DRX driver |
| authors: Dragan Savic, Milos Nikolic, Mihajlo Katona, Tao Ding, Paul Janssen |
| |
| The Linux DVB Driver for Micronas DRX39xx family (drx3933j) was |
| written by Devin Heitmueller <devin.heitmueller@kernellabs.com> |
| |
| This program is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 2 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program; if not, write to the Free Software |
| Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
| */ |
| |
| /*----------------------------------------------------------------------------- |
| INCLUDE FILES |
| ----------------------------------------------------------------------------*/ |
| |
| #define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__ |
| |
| #include <linux/module.h> |
| #include <linux/init.h> |
| #include <linux/string.h> |
| #include <linux/slab.h> |
| #include <asm/div64.h> |
| |
| #include "dvb_frontend.h" |
| #include "drx39xxj.h" |
| |
| #include "drxj.h" |
| #include "drxj_map.h" |
| |
| /*============================================================================*/ |
| /*=== DEFINES ================================================================*/ |
| /*============================================================================*/ |
| |
| #define DRX39XX_MAIN_FIRMWARE "dvb-fe-drxj-mc-1.0.8.fw" |
| |
| /** |
| * \brief Maximum u32 value. |
| */ |
| #ifndef MAX_U32 |
| #define MAX_U32 ((u32) (0xFFFFFFFFL)) |
| #endif |
| |
| /* Customer configurable hardware settings, etc */ |
| #ifndef MPEG_SERIAL_OUTPUT_PIN_DRIVE_STRENGTH |
| #define MPEG_SERIAL_OUTPUT_PIN_DRIVE_STRENGTH 0x02 |
| #endif |
| |
| #ifndef MPEG_PARALLEL_OUTPUT_PIN_DRIVE_STRENGTH |
| #define MPEG_PARALLEL_OUTPUT_PIN_DRIVE_STRENGTH 0x02 |
| #endif |
| |
| #ifndef MPEG_OUTPUT_CLK_DRIVE_STRENGTH |
| #define MPEG_OUTPUT_CLK_DRIVE_STRENGTH 0x06 |
| #endif |
| |
| #ifndef OOB_CRX_DRIVE_STRENGTH |
| #define OOB_CRX_DRIVE_STRENGTH 0x02 |
| #endif |
| |
| #ifndef OOB_DRX_DRIVE_STRENGTH |
| #define OOB_DRX_DRIVE_STRENGTH 0x02 |
| #endif |
| /**** START DJCOMBO patches to DRXJ registermap constants *********************/ |
| /**** registermap 200706071303 from drxj **************************************/ |
| #define ATV_TOP_CR_AMP_TH_FM 0x0 |
| #define ATV_TOP_CR_AMP_TH_L 0xA |
| #define ATV_TOP_CR_AMP_TH_LP 0xA |
| #define ATV_TOP_CR_AMP_TH_BG 0x8 |
| #define ATV_TOP_CR_AMP_TH_DK 0x8 |
| #define ATV_TOP_CR_AMP_TH_I 0x8 |
| #define ATV_TOP_CR_CONT_CR_D_MN 0x18 |
| #define ATV_TOP_CR_CONT_CR_D_FM 0x0 |
| #define ATV_TOP_CR_CONT_CR_D_L 0x20 |
| #define ATV_TOP_CR_CONT_CR_D_LP 0x20 |
| #define ATV_TOP_CR_CONT_CR_D_BG 0x18 |
| #define ATV_TOP_CR_CONT_CR_D_DK 0x18 |
| #define ATV_TOP_CR_CONT_CR_D_I 0x18 |
| #define ATV_TOP_CR_CONT_CR_I_MN 0x80 |
| #define ATV_TOP_CR_CONT_CR_I_FM 0x0 |
| #define ATV_TOP_CR_CONT_CR_I_L 0x80 |
| #define ATV_TOP_CR_CONT_CR_I_LP 0x80 |
| #define ATV_TOP_CR_CONT_CR_I_BG 0x80 |
| #define ATV_TOP_CR_CONT_CR_I_DK 0x80 |
| #define ATV_TOP_CR_CONT_CR_I_I 0x80 |
| #define ATV_TOP_CR_CONT_CR_P_MN 0x4 |
| #define ATV_TOP_CR_CONT_CR_P_FM 0x0 |
| #define ATV_TOP_CR_CONT_CR_P_L 0x4 |
| #define ATV_TOP_CR_CONT_CR_P_LP 0x4 |
| #define ATV_TOP_CR_CONT_CR_P_BG 0x4 |
| #define ATV_TOP_CR_CONT_CR_P_DK 0x4 |
| #define ATV_TOP_CR_CONT_CR_P_I 0x4 |
| #define ATV_TOP_CR_OVM_TH_MN 0xA0 |
| #define ATV_TOP_CR_OVM_TH_FM 0x0 |
| #define ATV_TOP_CR_OVM_TH_L 0xA0 |
| #define ATV_TOP_CR_OVM_TH_LP 0xA0 |
| #define ATV_TOP_CR_OVM_TH_BG 0xA0 |
| #define ATV_TOP_CR_OVM_TH_DK 0xA0 |
| #define ATV_TOP_CR_OVM_TH_I 0xA0 |
| #define ATV_TOP_EQU0_EQU_C0_FM 0x0 |
| #define ATV_TOP_EQU0_EQU_C0_L 0x3 |
| #define ATV_TOP_EQU0_EQU_C0_LP 0x3 |
| #define ATV_TOP_EQU0_EQU_C0_BG 0x7 |
| #define ATV_TOP_EQU0_EQU_C0_DK 0x0 |
| #define ATV_TOP_EQU0_EQU_C0_I 0x3 |
| #define ATV_TOP_EQU1_EQU_C1_FM 0x0 |
| #define ATV_TOP_EQU1_EQU_C1_L 0x1F6 |
| #define ATV_TOP_EQU1_EQU_C1_LP 0x1F6 |
| #define ATV_TOP_EQU1_EQU_C1_BG 0x197 |
| #define ATV_TOP_EQU1_EQU_C1_DK 0x198 |
| #define ATV_TOP_EQU1_EQU_C1_I 0x1F6 |
| #define ATV_TOP_EQU2_EQU_C2_FM 0x0 |
| #define ATV_TOP_EQU2_EQU_C2_L 0x28 |
| #define ATV_TOP_EQU2_EQU_C2_LP 0x28 |
| #define ATV_TOP_EQU2_EQU_C2_BG 0xC5 |
| #define ATV_TOP_EQU2_EQU_C2_DK 0xB0 |
| #define ATV_TOP_EQU2_EQU_C2_I 0x28 |
| #define ATV_TOP_EQU3_EQU_C3_FM 0x0 |
| #define ATV_TOP_EQU3_EQU_C3_L 0x192 |
| #define ATV_TOP_EQU3_EQU_C3_LP 0x192 |
| #define ATV_TOP_EQU3_EQU_C3_BG 0x12E |
| #define ATV_TOP_EQU3_EQU_C3_DK 0x18E |
| #define ATV_TOP_EQU3_EQU_C3_I 0x192 |
| #define ATV_TOP_STD_MODE_MN 0x0 |
| #define ATV_TOP_STD_MODE_FM 0x1 |
| #define ATV_TOP_STD_MODE_L 0x0 |
| #define ATV_TOP_STD_MODE_LP 0x0 |
| #define ATV_TOP_STD_MODE_BG 0x0 |
| #define ATV_TOP_STD_MODE_DK 0x0 |
| #define ATV_TOP_STD_MODE_I 0x0 |
| #define ATV_TOP_STD_VID_POL_MN 0x0 |
| #define ATV_TOP_STD_VID_POL_FM 0x0 |
| #define ATV_TOP_STD_VID_POL_L 0x2 |
| #define ATV_TOP_STD_VID_POL_LP 0x2 |
| #define ATV_TOP_STD_VID_POL_BG 0x0 |
| #define ATV_TOP_STD_VID_POL_DK 0x0 |
| #define ATV_TOP_STD_VID_POL_I 0x0 |
| #define ATV_TOP_VID_AMP_MN 0x380 |
| #define ATV_TOP_VID_AMP_FM 0x0 |
| #define ATV_TOP_VID_AMP_L 0xF50 |
| #define ATV_TOP_VID_AMP_LP 0xF50 |
| #define ATV_TOP_VID_AMP_BG 0x380 |
| #define ATV_TOP_VID_AMP_DK 0x394 |
| #define ATV_TOP_VID_AMP_I 0x3D8 |
| #define IQM_CF_OUT_ENA_OFDM__M 0x4 |
| #define IQM_FS_ADJ_SEL_B_QAM 0x1 |
| #define IQM_FS_ADJ_SEL_B_OFF 0x0 |
| #define IQM_FS_ADJ_SEL_B_VSB 0x2 |
| #define IQM_RC_ADJ_SEL_B_OFF 0x0 |
| #define IQM_RC_ADJ_SEL_B_QAM 0x1 |
| #define IQM_RC_ADJ_SEL_B_VSB 0x2 |
| /**** END DJCOMBO patches to DRXJ registermap *********************************/ |
| |
| #include "drx_driver_version.h" |
| |
| /* #define DRX_DEBUG */ |
| #ifdef DRX_DEBUG |
| #include <stdio.h> |
| #endif |
| |
| /*----------------------------------------------------------------------------- |
| ENUMS |
| ----------------------------------------------------------------------------*/ |
| |
| /*----------------------------------------------------------------------------- |
| DEFINES |
| ----------------------------------------------------------------------------*/ |
| #ifndef DRXJ_WAKE_UP_KEY |
| #define DRXJ_WAKE_UP_KEY (demod->my_i2c_dev_addr->i2c_addr) |
| #endif |
| |
| /** |
| * \def DRXJ_DEF_I2C_ADDR |
| * \brief Default I2C addres of a demodulator instance. |
| */ |
| #define DRXJ_DEF_I2C_ADDR (0x52) |
| |
| /** |
| * \def DRXJ_DEF_DEMOD_DEV_ID |
| * \brief Default device identifier of a demodultor instance. |
| */ |
| #define DRXJ_DEF_DEMOD_DEV_ID (1) |
| |
| /** |
| * \def DRXJ_SCAN_TIMEOUT |
| * \brief Timeout value for waiting on demod lock during channel scan (millisec). |
| */ |
| #define DRXJ_SCAN_TIMEOUT 1000 |
| |
| /** |
| * \def HI_I2C_DELAY |
| * \brief HI timing delay for I2C timing (in nano seconds) |
| * |
| * Used to compute HI_CFG_DIV |
| */ |
| #define HI_I2C_DELAY 42 |
| |
| /** |
| * \def HI_I2C_BRIDGE_DELAY |
| * \brief HI timing delay for I2C timing (in nano seconds) |
| * |
| * Used to compute HI_CFG_BDL |
| */ |
| #define HI_I2C_BRIDGE_DELAY 750 |
| |
| /** |
| * \brief Time Window for MER and SER Measurement in Units of Segment duration. |
| */ |
| #define VSB_TOP_MEASUREMENT_PERIOD 64 |
| #define SYMBOLS_PER_SEGMENT 832 |
| |
| /** |
| * \brief bit rate and segment rate constants used for SER and BER. |
| */ |
| /* values taken from the QAM microcode */ |
| #define DRXJ_QAM_SL_SIG_POWER_QAM_UNKNOWN 0 |
| #define DRXJ_QAM_SL_SIG_POWER_QPSK 32768 |
| #define DRXJ_QAM_SL_SIG_POWER_QAM8 24576 |
| #define DRXJ_QAM_SL_SIG_POWER_QAM16 40960 |
| #define DRXJ_QAM_SL_SIG_POWER_QAM32 20480 |
| #define DRXJ_QAM_SL_SIG_POWER_QAM64 43008 |
| #define DRXJ_QAM_SL_SIG_POWER_QAM128 20992 |
| #define DRXJ_QAM_SL_SIG_POWER_QAM256 43520 |
| /** |
| * \brief Min supported symbolrates. |
| */ |
| #ifndef DRXJ_QAM_SYMBOLRATE_MIN |
| #define DRXJ_QAM_SYMBOLRATE_MIN (520000) |
| #endif |
| |
| /** |
| * \brief Max supported symbolrates. |
| */ |
| #ifndef DRXJ_QAM_SYMBOLRATE_MAX |
| #define DRXJ_QAM_SYMBOLRATE_MAX (7233000) |
| #endif |
| |
| /** |
| * \def DRXJ_QAM_MAX_WAITTIME |
| * \brief Maximal wait time for QAM auto constellation in ms |
| */ |
| #ifndef DRXJ_QAM_MAX_WAITTIME |
| #define DRXJ_QAM_MAX_WAITTIME 900 |
| #endif |
| |
| #ifndef DRXJ_QAM_FEC_LOCK_WAITTIME |
| #define DRXJ_QAM_FEC_LOCK_WAITTIME 150 |
| #endif |
| |
| #ifndef DRXJ_QAM_DEMOD_LOCK_EXT_WAITTIME |
| #define DRXJ_QAM_DEMOD_LOCK_EXT_WAITTIME 200 |
| #endif |
| |
| /** |
| * \def SCU status and results |
| * \brief SCU |
| */ |
| #define DRX_SCU_READY 0 |
| #define DRXJ_MAX_WAITTIME 100 /* ms */ |
| #define FEC_RS_MEASUREMENT_PERIOD 12894 /* 1 sec */ |
| #define FEC_RS_MEASUREMENT_PRESCALE 1 /* n sec */ |
| |
| /** |
| * \def DRX_AUD_MAX_DEVIATION |
| * \brief Needed for calculation of prescale feature in AUD |
| */ |
| #ifndef DRXJ_AUD_MAX_FM_DEVIATION |
| #define DRXJ_AUD_MAX_FM_DEVIATION 100 /* kHz */ |
| #endif |
| |
| /** |
| * \brief Needed for calculation of NICAM prescale feature in AUD |
| */ |
| #ifndef DRXJ_AUD_MAX_NICAM_PRESCALE |
| #define DRXJ_AUD_MAX_NICAM_PRESCALE (9) /* dB */ |
| #endif |
| |
| /** |
| * \brief Needed for calculation of NICAM prescale feature in AUD |
| */ |
| #ifndef DRXJ_AUD_MAX_WAITTIME |
| #define DRXJ_AUD_MAX_WAITTIME 250 /* ms */ |
| #endif |
| |
| /* ATV config changed flags */ |
| #define DRXJ_ATV_CHANGED_COEF (0x00000001UL) |
| #define DRXJ_ATV_CHANGED_PEAK_FLT (0x00000008UL) |
| #define DRXJ_ATV_CHANGED_NOISE_FLT (0x00000010UL) |
| #define DRXJ_ATV_CHANGED_OUTPUT (0x00000020UL) |
| #define DRXJ_ATV_CHANGED_SIF_ATT (0x00000040UL) |
| |
| /* UIO define */ |
| #define DRX_UIO_MODE_FIRMWARE_SMA DRX_UIO_MODE_FIRMWARE0 |
| #define DRX_UIO_MODE_FIRMWARE_SAW DRX_UIO_MODE_FIRMWARE1 |
| |
| /* |
| * MICROCODE RELATED DEFINES |
| */ |
| |
| /* Magic word for checking correct Endianess of microcode data */ |
| #define DRX_UCODE_MAGIC_WORD ((((u16)'H')<<8)+((u16)'L')) |
| |
| /* CRC flag in ucode header, flags field. */ |
| #define DRX_UCODE_CRC_FLAG (0x0001) |
| |
| /* |
| * Maximum size of buffer used to verify the microcode. |
| * Must be an even number |
| */ |
| #define DRX_UCODE_MAX_BUF_SIZE (DRXDAP_MAX_RCHUNKSIZE) |
| |
| #if DRX_UCODE_MAX_BUF_SIZE & 1 |
| #error DRX_UCODE_MAX_BUF_SIZE must be an even number |
| #endif |
| |
| /* |
| * Power mode macros |
| */ |
| |
| #define DRX_ISPOWERDOWNMODE(mode) ((mode == DRX_POWER_MODE_9) || \ |
| (mode == DRX_POWER_MODE_10) || \ |
| (mode == DRX_POWER_MODE_11) || \ |
| (mode == DRX_POWER_MODE_12) || \ |
| (mode == DRX_POWER_MODE_13) || \ |
| (mode == DRX_POWER_MODE_14) || \ |
| (mode == DRX_POWER_MODE_15) || \ |
| (mode == DRX_POWER_MODE_16) || \ |
| (mode == DRX_POWER_DOWN)) |
| |
| /* Pin safe mode macro */ |
| #define DRXJ_PIN_SAFE_MODE 0x0000 |
| /*============================================================================*/ |
| /*=== GLOBAL VARIABLEs =======================================================*/ |
| /*============================================================================*/ |
| /** |
| */ |
| |
| /** |
| * \brief Temporary register definitions. |
| * (register definitions that are not yet available in register master) |
| */ |
| |
| /******************************************************************************/ |
| /* Audio block 0x103 is write only. To avoid shadowing in driver accessing */ |
| /* RAM adresses directly. This must be READ ONLY to avoid problems. */ |
| /* Writing to the interface adresses is more than only writing the RAM */ |
| /* locations */ |
| /******************************************************************************/ |
| /** |
| * \brief RAM location of MODUS registers |
| */ |
| #define AUD_DEM_RAM_MODUS_HI__A 0x10204A3 |
| #define AUD_DEM_RAM_MODUS_HI__M 0xF000 |
| |
| #define AUD_DEM_RAM_MODUS_LO__A 0x10204A4 |
| #define AUD_DEM_RAM_MODUS_LO__M 0x0FFF |
| |
| /** |
| * \brief RAM location of I2S config registers |
| */ |
| #define AUD_DEM_RAM_I2S_CONFIG1__A 0x10204B1 |
| #define AUD_DEM_RAM_I2S_CONFIG2__A 0x10204B2 |
| |
| /** |
| * \brief RAM location of DCO config registers |
| */ |
| #define AUD_DEM_RAM_DCO_B_HI__A 0x1020461 |
| #define AUD_DEM_RAM_DCO_B_LO__A 0x1020462 |
| #define AUD_DEM_RAM_DCO_A_HI__A 0x1020463 |
| #define AUD_DEM_RAM_DCO_A_LO__A 0x1020464 |
| |
| /** |
| * \brief RAM location of Threshold registers |
| */ |
| #define AUD_DEM_RAM_NICAM_THRSHLD__A 0x102045A |
| #define AUD_DEM_RAM_A2_THRSHLD__A 0x10204BB |
| #define AUD_DEM_RAM_BTSC_THRSHLD__A 0x10204A6 |
| |
| /** |
| * \brief RAM location of Carrier Threshold registers |
| */ |
| #define AUD_DEM_RAM_CM_A_THRSHLD__A 0x10204AF |
| #define AUD_DEM_RAM_CM_B_THRSHLD__A 0x10204B0 |
| |
| /** |
| * \brief FM Matrix register fix |
| */ |
| #ifdef AUD_DEM_WR_FM_MATRIX__A |
| #undef AUD_DEM_WR_FM_MATRIX__A |
| #endif |
| #define AUD_DEM_WR_FM_MATRIX__A 0x105006F |
| |
| /*============================================================================*/ |
| /** |
| * \brief Defines required for audio |
| */ |
| #define AUD_VOLUME_ZERO_DB 115 |
| #define AUD_VOLUME_DB_MIN -60 |
| #define AUD_VOLUME_DB_MAX 12 |
| #define AUD_CARRIER_STRENGTH_QP_0DB 0x4000 |
| #define AUD_CARRIER_STRENGTH_QP_0DB_LOG10T100 421 |
| #define AUD_MAX_AVC_REF_LEVEL 15 |
| #define AUD_I2S_FREQUENCY_MAX 48000UL |
| #define AUD_I2S_FREQUENCY_MIN 12000UL |
| #define AUD_RDS_ARRAY_SIZE 18 |
| |
| /** |
| * \brief Needed for calculation of prescale feature in AUD |
| */ |
| #ifndef DRX_AUD_MAX_FM_DEVIATION |
| #define DRX_AUD_MAX_FM_DEVIATION (100) /* kHz */ |
| #endif |
| |
| /** |
| * \brief Needed for calculation of NICAM prescale feature in AUD |
| */ |
| #ifndef DRX_AUD_MAX_NICAM_PRESCALE |
| #define DRX_AUD_MAX_NICAM_PRESCALE (9) /* dB */ |
| #endif |
| |
| /*============================================================================*/ |
| /* Values for I2S Master/Slave pin configurations */ |
| #define SIO_PDR_I2S_CL_CFG_MODE__MASTER 0x0004 |
| #define SIO_PDR_I2S_CL_CFG_DRIVE__MASTER 0x0008 |
| #define SIO_PDR_I2S_CL_CFG_MODE__SLAVE 0x0004 |
| #define SIO_PDR_I2S_CL_CFG_DRIVE__SLAVE 0x0000 |
| |
| #define SIO_PDR_I2S_DA_CFG_MODE__MASTER 0x0003 |
| #define SIO_PDR_I2S_DA_CFG_DRIVE__MASTER 0x0008 |
| #define SIO_PDR_I2S_DA_CFG_MODE__SLAVE 0x0003 |
| #define SIO_PDR_I2S_DA_CFG_DRIVE__SLAVE 0x0008 |
| |
| #define SIO_PDR_I2S_WS_CFG_MODE__MASTER 0x0004 |
| #define SIO_PDR_I2S_WS_CFG_DRIVE__MASTER 0x0008 |
| #define SIO_PDR_I2S_WS_CFG_MODE__SLAVE 0x0004 |
| #define SIO_PDR_I2S_WS_CFG_DRIVE__SLAVE 0x0000 |
| |
| /*============================================================================*/ |
| /*=== REGISTER ACCESS MACROS =================================================*/ |
| /*============================================================================*/ |
| |
| /** |
| * This macro is used to create byte arrays for block writes. |
| * Block writes speed up I2C traffic between host and demod. |
| * The macro takes care of the required byte order in a 16 bits word. |
| * x -> lowbyte(x), highbyte(x) |
| */ |
| #define DRXJ_16TO8(x) ((u8) (((u16)x) & 0xFF)), \ |
| ((u8)((((u16)x)>>8)&0xFF)) |
| /** |
| * This macro is used to convert byte array to 16 bit register value for block read. |
| * Block read speed up I2C traffic between host and demod. |
| * The macro takes care of the required byte order in a 16 bits word. |
| */ |
| #define DRXJ_8TO16(x) ((u16) (x[0] | (x[1] << 8))) |
| |
| /*============================================================================*/ |
| /*=== MISC DEFINES ===========================================================*/ |
| /*============================================================================*/ |
| |
| /*============================================================================*/ |
| /*=== HI COMMAND RELATED DEFINES =============================================*/ |
| /*============================================================================*/ |
| |
| /** |
| * \brief General maximum number of retries for ucode command interfaces |
| */ |
| #define DRXJ_MAX_RETRIES (100) |
| |
| /*============================================================================*/ |
| /*=== STANDARD RELATED MACROS ================================================*/ |
| /*============================================================================*/ |
| |
| #define DRXJ_ISATVSTD(std) ((std == DRX_STANDARD_PAL_SECAM_BG) || \ |
| (std == DRX_STANDARD_PAL_SECAM_DK) || \ |
| (std == DRX_STANDARD_PAL_SECAM_I) || \ |
| (std == DRX_STANDARD_PAL_SECAM_L) || \ |
| (std == DRX_STANDARD_PAL_SECAM_LP) || \ |
| (std == DRX_STANDARD_NTSC) || \ |
| (std == DRX_STANDARD_FM)) |
| |
| #define DRXJ_ISQAMSTD(std) ((std == DRX_STANDARD_ITU_A) || \ |
| (std == DRX_STANDARD_ITU_B) || \ |
| (std == DRX_STANDARD_ITU_C) || \ |
| (std == DRX_STANDARD_ITU_D)) |
| |
| /*----------------------------------------------------------------------------- |
| GLOBAL VARIABLES |
| ----------------------------------------------------------------------------*/ |
| /* |
| * DRXJ DAP structures |
| */ |
| |
| static int drxdap_fasi_read_block(struct i2c_device_addr *dev_addr, |
| u32 addr, |
| u16 datasize, |
| u8 *data, u32 flags); |
| |
| |
| static int drxj_dap_read_modify_write_reg16(struct i2c_device_addr *dev_addr, |
| u32 waddr, |
| u32 raddr, |
| u16 wdata, u16 *rdata); |
| |
| static int drxj_dap_read_reg16(struct i2c_device_addr *dev_addr, |
| u32 addr, |
| u16 *data, u32 flags); |
| |
| static int drxdap_fasi_read_reg32(struct i2c_device_addr *dev_addr, |
| u32 addr, |
| u32 *data, u32 flags); |
| |
| static int drxdap_fasi_write_block(struct i2c_device_addr *dev_addr, |
| u32 addr, |
| u16 datasize, |
| u8 *data, u32 flags); |
| |
| static int drxj_dap_write_reg16(struct i2c_device_addr *dev_addr, |
| u32 addr, |
| u16 data, u32 flags); |
| |
| static int drxdap_fasi_write_reg32(struct i2c_device_addr *dev_addr, |
| u32 addr, |
| u32 data, u32 flags); |
| |
| static struct drxj_data drxj_data_g = { |
| false, /* has_lna : true if LNA (aka PGA) present */ |
| false, /* has_oob : true if OOB supported */ |
| false, /* has_ntsc: true if NTSC supported */ |
| false, /* has_btsc: true if BTSC supported */ |
| false, /* has_smatx: true if SMA_TX pin is available */ |
| false, /* has_smarx: true if SMA_RX pin is available */ |
| false, /* has_gpio : true if GPIO pin is available */ |
| false, /* has_irqn : true if IRQN pin is available */ |
| 0, /* mfx A1/A2/A... */ |
| |
| /* tuner settings */ |
| false, /* tuner mirrors RF signal */ |
| /* standard/channel settings */ |
| DRX_STANDARD_UNKNOWN, /* current standard */ |
| DRX_CONSTELLATION_AUTO, /* constellation */ |
| 0, /* frequency in KHz */ |
| DRX_BANDWIDTH_UNKNOWN, /* curr_bandwidth */ |
| DRX_MIRROR_NO, /* mirror */ |
| |
| /* signal quality information: */ |
| /* default values taken from the QAM Programming guide */ |
| /* fec_bits_desired should not be less than 4000000 */ |
| 4000000, /* fec_bits_desired */ |
| 5, /* fec_vd_plen */ |
| 4, /* qam_vd_prescale */ |
| 0xFFFF, /* qamVDPeriod */ |
| 204 * 8, /* fec_rs_plen annex A */ |
| 1, /* fec_rs_prescale */ |
| FEC_RS_MEASUREMENT_PERIOD, /* fec_rs_period */ |
| true, /* reset_pkt_err_acc */ |
| 0, /* pkt_err_acc_start */ |
| |
| /* HI configuration */ |
| 0, /* hi_cfg_timing_div */ |
| 0, /* hi_cfg_bridge_delay */ |
| 0, /* hi_cfg_wake_up_key */ |
| 0, /* hi_cfg_ctrl */ |
| 0, /* HICfgTimeout */ |
| /* UIO configuartion */ |
| DRX_UIO_MODE_DISABLE, /* uio_sma_rx_mode */ |
| DRX_UIO_MODE_DISABLE, /* uio_sma_tx_mode */ |
| DRX_UIO_MODE_DISABLE, /* uioASELMode */ |
| DRX_UIO_MODE_DISABLE, /* uio_irqn_mode */ |
| /* FS setting */ |
| 0UL, /* iqm_fs_rate_ofs */ |
| false, /* pos_image */ |
| /* RC setting */ |
| 0UL, /* iqm_rc_rate_ofs */ |
| /* AUD information */ |
| /* false, * flagSetAUDdone */ |
| /* false, * detectedRDS */ |
| /* true, * flagASDRequest */ |
| /* false, * flagHDevClear */ |
| /* false, * flagHDevSet */ |
| /* (u16) 0xFFF, * rdsLastCount */ |
| |
| /* ATV configuartion */ |
| 0UL, /* flags cfg changes */ |
| /* shadow of ATV_TOP_EQU0__A */ |
| {-5, |
| ATV_TOP_EQU0_EQU_C0_FM, |
| ATV_TOP_EQU0_EQU_C0_L, |
| ATV_TOP_EQU0_EQU_C0_LP, |
| ATV_TOP_EQU0_EQU_C0_BG, |
| ATV_TOP_EQU0_EQU_C0_DK, |
| ATV_TOP_EQU0_EQU_C0_I}, |
| /* shadow of ATV_TOP_EQU1__A */ |
| {-50, |
| ATV_TOP_EQU1_EQU_C1_FM, |
| ATV_TOP_EQU1_EQU_C1_L, |
| ATV_TOP_EQU1_EQU_C1_LP, |
| ATV_TOP_EQU1_EQU_C1_BG, |
| ATV_TOP_EQU1_EQU_C1_DK, |
| ATV_TOP_EQU1_EQU_C1_I}, |
| /* shadow of ATV_TOP_EQU2__A */ |
| {210, |
| ATV_TOP_EQU2_EQU_C2_FM, |
| ATV_TOP_EQU2_EQU_C2_L, |
| ATV_TOP_EQU2_EQU_C2_LP, |
| ATV_TOP_EQU2_EQU_C2_BG, |
| ATV_TOP_EQU2_EQU_C2_DK, |
| ATV_TOP_EQU2_EQU_C2_I}, |
| /* shadow of ATV_TOP_EQU3__A */ |
| {-160, |
| ATV_TOP_EQU3_EQU_C3_FM, |
| ATV_TOP_EQU3_EQU_C3_L, |
| ATV_TOP_EQU3_EQU_C3_LP, |
| ATV_TOP_EQU3_EQU_C3_BG, |
| ATV_TOP_EQU3_EQU_C3_DK, |
| ATV_TOP_EQU3_EQU_C3_I}, |
| false, /* flag: true=bypass */ |
| ATV_TOP_VID_PEAK__PRE, /* shadow of ATV_TOP_VID_PEAK__A */ |
| ATV_TOP_NOISE_TH__PRE, /* shadow of ATV_TOP_NOISE_TH__A */ |
| true, /* flag CVBS ouput enable */ |
| false, /* flag SIF ouput enable */ |
| DRXJ_SIF_ATTENUATION_0DB, /* current SIF att setting */ |
| { /* qam_rf_agc_cfg */ |
| DRX_STANDARD_ITU_B, /* standard */ |
| DRX_AGC_CTRL_AUTO, /* ctrl_mode */ |
| 0, /* output_level */ |
| 0, /* min_output_level */ |
| 0xFFFF, /* max_output_level */ |
| 0x0000, /* speed */ |
| 0x0000, /* top */ |
| 0x0000 /* c.o.c. */ |
| }, |
| { /* qam_if_agc_cfg */ |
| DRX_STANDARD_ITU_B, /* standard */ |
| DRX_AGC_CTRL_AUTO, /* ctrl_mode */ |
| 0, /* output_level */ |
| 0, /* min_output_level */ |
| 0xFFFF, /* max_output_level */ |
| 0x0000, /* speed */ |
| 0x0000, /* top (don't care) */ |
| 0x0000 /* c.o.c. (don't care) */ |
| }, |
| { /* vsb_rf_agc_cfg */ |
| DRX_STANDARD_8VSB, /* standard */ |
| DRX_AGC_CTRL_AUTO, /* ctrl_mode */ |
| 0, /* output_level */ |
| 0, /* min_output_level */ |
| 0xFFFF, /* max_output_level */ |
| 0x0000, /* speed */ |
| 0x0000, /* top (don't care) */ |
| 0x0000 /* c.o.c. (don't care) */ |
| }, |
| { /* vsb_if_agc_cfg */ |
| DRX_STANDARD_8VSB, /* standard */ |
| DRX_AGC_CTRL_AUTO, /* ctrl_mode */ |
| 0, /* output_level */ |
| 0, /* min_output_level */ |
| 0xFFFF, /* max_output_level */ |
| 0x0000, /* speed */ |
| 0x0000, /* top (don't care) */ |
| 0x0000 /* c.o.c. (don't care) */ |
| }, |
| 0, /* qam_pga_cfg */ |
| 0, /* vsb_pga_cfg */ |
| { /* qam_pre_saw_cfg */ |
| DRX_STANDARD_ITU_B, /* standard */ |
| 0, /* reference */ |
| false /* use_pre_saw */ |
| }, |
| { /* vsb_pre_saw_cfg */ |
| DRX_STANDARD_8VSB, /* standard */ |
| 0, /* reference */ |
| false /* use_pre_saw */ |
| }, |
| |
| /* Version information */ |
| #ifndef _CH_ |
| { |
| "01234567890", /* human readable version microcode */ |
| "01234567890" /* human readable version device specific code */ |
| }, |
| { |
| { /* struct drx_version for microcode */ |
| DRX_MODULE_UNKNOWN, |
| (char *)(NULL), |
| 0, |
| 0, |
| 0, |
| (char *)(NULL) |
| }, |
| { /* struct drx_version for device specific code */ |
| DRX_MODULE_UNKNOWN, |
| (char *)(NULL), |
| 0, |
| 0, |
| 0, |
| (char *)(NULL) |
| } |
| }, |
| { |
| { /* struct drx_version_list for microcode */ |
| (struct drx_version *) (NULL), |
| (struct drx_version_list *) (NULL) |
| }, |
| { /* struct drx_version_list for device specific code */ |
| (struct drx_version *) (NULL), |
| (struct drx_version_list *) (NULL) |
| } |
| }, |
| #endif |
| false, /* smart_ant_inverted */ |
| /* Tracking filter setting for OOB */ |
| { |
| 12000, |
| 9300, |
| 6600, |
| 5280, |
| 3700, |
| 3000, |
| 2000, |
| 0}, |
| false, /* oob_power_on */ |
| 0, /* mpeg_ts_static_bitrate */ |
| false, /* disable_te_ihandling */ |
| false, /* bit_reverse_mpeg_outout */ |
| DRXJ_MPEGOUTPUT_CLOCK_RATE_AUTO, /* mpeg_output_clock_rate */ |
| DRXJ_MPEG_START_WIDTH_1CLKCYC, /* mpeg_start_width */ |
| |
| /* Pre SAW & Agc configuration for ATV */ |
| { |
| DRX_STANDARD_NTSC, /* standard */ |
| 7, /* reference */ |
| true /* use_pre_saw */ |
| }, |
| { /* ATV RF-AGC */ |
| DRX_STANDARD_NTSC, /* standard */ |
| DRX_AGC_CTRL_AUTO, /* ctrl_mode */ |
| 0, /* output_level */ |
| 0, /* min_output_level (d.c.) */ |
| 0, /* max_output_level (d.c.) */ |
| 3, /* speed */ |
| 9500, /* top */ |
| 4000 /* cut-off current */ |
| }, |
| { /* ATV IF-AGC */ |
| DRX_STANDARD_NTSC, /* standard */ |
| DRX_AGC_CTRL_AUTO, /* ctrl_mode */ |
| 0, /* output_level */ |
| 0, /* min_output_level (d.c.) */ |
| 0, /* max_output_level (d.c.) */ |
| 3, /* speed */ |
| 2400, /* top */ |
| 0 /* c.o.c. (d.c.) */ |
| }, |
| 140, /* ATV PGA config */ |
| 0, /* curr_symbol_rate */ |
| |
| false, /* pdr_safe_mode */ |
| SIO_PDR_GPIO_CFG__PRE, /* pdr_safe_restore_val_gpio */ |
| SIO_PDR_VSYNC_CFG__PRE, /* pdr_safe_restore_val_v_sync */ |
| SIO_PDR_SMA_RX_CFG__PRE, /* pdr_safe_restore_val_sma_rx */ |
| SIO_PDR_SMA_TX_CFG__PRE, /* pdr_safe_restore_val_sma_tx */ |
| |
| 4, /* oob_pre_saw */ |
| DRXJ_OOB_LO_POW_MINUS10DB, /* oob_lo_pow */ |
| { |
| false /* aud_data, only first member */ |
| }, |
| }; |
| |
| /** |
| * \var drxj_default_addr_g |
| * \brief Default I2C address and device identifier. |
| */ |
| static struct i2c_device_addr drxj_default_addr_g = { |
| DRXJ_DEF_I2C_ADDR, /* i2c address */ |
| DRXJ_DEF_DEMOD_DEV_ID /* device id */ |
| }; |
| |
| /** |
| * \var drxj_default_comm_attr_g |
| * \brief Default common attributes of a drxj demodulator instance. |
| */ |
| static struct drx_common_attr drxj_default_comm_attr_g = { |
| NULL, /* ucode file */ |
| true, /* ucode verify switch */ |
| {0}, /* version record */ |
| |
| 44000, /* IF in kHz in case no tuner instance is used */ |
| (151875 - 0), /* system clock frequency in kHz */ |
| 0, /* oscillator frequency kHz */ |
| 0, /* oscillator deviation in ppm, signed */ |
| false, /* If true mirror frequency spectrum */ |
| { |
| /* MPEG output configuration */ |
| true, /* If true, enable MPEG ouput */ |
| false, /* If true, insert RS byte */ |
| false, /* If true, parallel out otherwise serial */ |
| false, /* If true, invert DATA signals */ |
| false, /* If true, invert ERR signal */ |
| false, /* If true, invert STR signals */ |
| false, /* If true, invert VAL signals */ |
| false, /* If true, invert CLK signals */ |
| true, /* If true, static MPEG clockrate will |
| be used, otherwise clockrate will |
| adapt to the bitrate of the TS */ |
| 19392658UL, /* Maximum bitrate in b/s in case |
| static clockrate is selected */ |
| DRX_MPEG_STR_WIDTH_1 /* MPEG Start width in clock cycles */ |
| }, |
| /* Initilisations below can be ommited, they require no user input and |
| are initialy 0, NULL or false. The compiler will initialize them to these |
| values when ommited. */ |
| false, /* is_opened */ |
| |
| /* SCAN */ |
| NULL, /* no scan params yet */ |
| 0, /* current scan index */ |
| 0, /* next scan frequency */ |
| false, /* scan ready flag */ |
| 0, /* max channels to scan */ |
| 0, /* nr of channels scanned */ |
| NULL, /* default scan function */ |
| NULL, /* default context pointer */ |
| 0, /* millisec to wait for demod lock */ |
| DRXJ_DEMOD_LOCK, /* desired lock */ |
| false, |
| |
| /* Power management */ |
| DRX_POWER_UP, |
| |
| /* Tuner */ |
| 1, /* nr of I2C port to wich tuner is */ |
| 0L, /* minimum RF input frequency, in kHz */ |
| 0L, /* maximum RF input frequency, in kHz */ |
| false, /* Rf Agc Polarity */ |
| false, /* If Agc Polarity */ |
| false, /* tuner slow mode */ |
| |
| { /* current channel (all 0) */ |
| 0UL /* channel.frequency */ |
| }, |
| DRX_STANDARD_UNKNOWN, /* current standard */ |
| DRX_STANDARD_UNKNOWN, /* previous standard */ |
| DRX_STANDARD_UNKNOWN, /* di_cache_standard */ |
| false, /* use_bootloader */ |
| 0UL, /* capabilities */ |
| 0 /* mfx */ |
| }; |
| |
| /** |
| * \var drxj_default_demod_g |
| * \brief Default drxj demodulator instance. |
| */ |
| static struct drx_demod_instance drxj_default_demod_g = { |
| &drxj_default_addr_g, /* i2c address & device id */ |
| &drxj_default_comm_attr_g, /* demod common attributes */ |
| &drxj_data_g /* demod device specific attributes */ |
| }; |
| |
| /** |
| * \brief Default audio data structure for DRK demodulator instance. |
| * |
| * This structure is DRXK specific. |
| * |
| */ |
| static struct drx_aud_data drxj_default_aud_data_g = { |
| false, /* audio_is_active */ |
| DRX_AUD_STANDARD_AUTO, /* audio_standard */ |
| |
| /* i2sdata */ |
| { |
| false, /* output_enable */ |
| 48000, /* frequency */ |
| DRX_I2S_MODE_MASTER, /* mode */ |
| DRX_I2S_WORDLENGTH_32, /* word_length */ |
| DRX_I2S_POLARITY_RIGHT, /* polarity */ |
| DRX_I2S_FORMAT_WS_WITH_DATA /* format */ |
| }, |
| /* volume */ |
| { |
| true, /* mute; */ |
| 0, /* volume */ |
| DRX_AUD_AVC_OFF, /* avc_mode */ |
| 0, /* avc_ref_level */ |
| DRX_AUD_AVC_MAX_GAIN_12DB, /* avc_max_gain */ |
| DRX_AUD_AVC_MAX_ATTEN_24DB, /* avc_max_atten */ |
| 0, /* strength_left */ |
| 0 /* strength_right */ |
| }, |
| DRX_AUD_AUTO_SOUND_SELECT_ON_CHANGE_ON, /* auto_sound */ |
| /* ass_thresholds */ |
| { |
| 440, /* A2 */ |
| 12, /* BTSC */ |
| 700, /* NICAM */ |
| }, |
| /* carrier */ |
| { |
| /* a */ |
| { |
| 42, /* thres */ |
| DRX_NO_CARRIER_NOISE, /* opt */ |
| 0, /* shift */ |
| 0 /* dco */ |
| }, |
| /* b */ |
| { |
| 42, /* thres */ |
| DRX_NO_CARRIER_MUTE, /* opt */ |
| 0, /* shift */ |
| 0 /* dco */ |
| }, |
| |
| }, |
| /* mixer */ |
| { |
| DRX_AUD_SRC_STEREO_OR_A, /* source_i2s */ |
| DRX_AUD_I2S_MATRIX_STEREO, /* matrix_i2s */ |
| DRX_AUD_FM_MATRIX_SOUND_A /* matrix_fm */ |
| }, |
| DRX_AUD_DEVIATION_NORMAL, /* deviation */ |
| DRX_AUD_AVSYNC_OFF, /* av_sync */ |
| |
| /* prescale */ |
| { |
| DRX_AUD_MAX_FM_DEVIATION, /* fm_deviation */ |
| DRX_AUD_MAX_NICAM_PRESCALE /* nicam_gain */ |
| }, |
| DRX_AUD_FM_DEEMPH_75US, /* deemph */ |
| DRX_BTSC_STEREO, /* btsc_detect */ |
| 0, /* rds_data_counter */ |
| false /* rds_data_present */ |
| }; |
| |
| /*----------------------------------------------------------------------------- |
| STRUCTURES |
| ----------------------------------------------------------------------------*/ |
| struct drxjeq_stat { |
| u16 eq_mse; |
| u8 eq_mode; |
| u8 eq_ctrl; |
| u8 eq_stat; |
| }; |
| |
| /* HI command */ |
| struct drxj_hi_cmd { |
| u16 cmd; |
| u16 param1; |
| u16 param2; |
| u16 param3; |
| u16 param4; |
| u16 param5; |
| u16 param6; |
| }; |
| |
| /*============================================================================*/ |
| /*=== MICROCODE RELATED STRUCTURES ===========================================*/ |
| /*============================================================================*/ |
| |
| /** |
| * struct drxu_code_block_hdr - Structure of the microcode block headers |
| * |
| * @addr: Destination address of the data in this block |
| * @size: Size of the block data following this header counted in |
| * 16 bits words |
| * @CRC: CRC value of the data block, only valid if CRC flag is |
| * set. |
| */ |
| struct drxu_code_block_hdr { |
| u32 addr; |
| u16 size; |
| u16 flags; |
| u16 CRC; |
| }; |
| |
| /*----------------------------------------------------------------------------- |
| FUNCTIONS |
| ----------------------------------------------------------------------------*/ |
| /* Some prototypes */ |
| static int |
| hi_command(struct i2c_device_addr *dev_addr, |
| const struct drxj_hi_cmd *cmd, u16 *result); |
| |
| static int |
| ctrl_lock_status(struct drx_demod_instance *demod, enum drx_lock_status *lock_stat); |
| |
| static int |
| ctrl_power_mode(struct drx_demod_instance *demod, enum drx_power_mode *mode); |
| |
| static int power_down_aud(struct drx_demod_instance *demod); |
| |
| static int |
| ctrl_set_cfg_pre_saw(struct drx_demod_instance *demod, struct drxj_cfg_pre_saw *pre_saw); |
| |
| static int |
| ctrl_set_cfg_afe_gain(struct drx_demod_instance *demod, struct drxj_cfg_afe_gain *afe_gain); |
| |
| /*============================================================================*/ |
| /*============================================================================*/ |
| /*== HELPER FUNCTIONS ==*/ |
| /*============================================================================*/ |
| /*============================================================================*/ |
| |
| |
| /*============================================================================*/ |
| |
| /* |
| * \fn u32 frac28(u32 N, u32 D) |
| * \brief Compute: (1<<28)*N/D |
| * \param N 32 bits |
| * \param D 32 bits |
| * \return (1<<28)*N/D |
| * This function is used to avoid floating-point calculations as they may |
| * not be present on the target platform. |
| |
| * frac28 performs an unsigned 28/28 bits division to 32-bit fixed point |
| * fraction used for setting the Frequency Shifter registers. |
| * N and D can hold numbers up to width: 28-bits. |
| * The 4 bits integer part and the 28 bits fractional part are calculated. |
| |
| * Usage condition: ((1<<28)*n)/d < ((1<<32)-1) => (n/d) < 15.999 |
| |
| * N: 0...(1<<28)-1 = 268435454 |
| * D: 0...(1<<28)-1 |
| * Q: 0...(1<<32)-1 |
| */ |
| static u32 frac28(u32 N, u32 D) |
| { |
| int i = 0; |
| u32 Q1 = 0; |
| u32 R0 = 0; |
| |
| R0 = (N % D) << 4; /* 32-28 == 4 shifts possible at max */ |
| Q1 = N / D; /* integer part, only the 4 least significant bits |
| will be visible in the result */ |
| |
| /* division using radix 16, 7 nibbles in the result */ |
| for (i = 0; i < 7; i++) { |
| Q1 = (Q1 << 4) | R0 / D; |
| R0 = (R0 % D) << 4; |
| } |
| /* rounding */ |
| if ((R0 >> 3) >= D) |
| Q1++; |
| |
| return Q1; |
| } |
| |
| /** |
| * \fn u32 log1_times100( u32 x) |
| * \brief Compute: 100*log10(x) |
| * \param x 32 bits |
| * \return 100*log10(x) |
| * |
| * 100*log10(x) |
| * = 100*(log2(x)/log2(10))) |
| * = (100*(2^15)*log2(x))/((2^15)*log2(10)) |
| * = ((200*(2^15)*log2(x))/((2^15)*log2(10)))/2 |
| * = ((200*(2^15)*(log2(x/y)+log2(y)))/((2^15)*log2(10)))/2 |
| * = ((200*(2^15)*log2(x/y))+(200*(2^15)*log2(y)))/((2^15)*log2(10)))/2 |
| * |
| * where y = 2^k and 1<= (x/y) < 2 |
| */ |
| |
| static u32 log1_times100(u32 x) |
| { |
| static const u8 scale = 15; |
| static const u8 index_width = 5; |
| /* |
| log2lut[n] = (1<<scale) * 200 * log2( 1.0 + ( (1.0/(1<<INDEXWIDTH)) * n )) |
| 0 <= n < ((1<<INDEXWIDTH)+1) |
| */ |
| |
| static const u32 log2lut[] = { |
| 0, /* 0.000000 */ |
| 290941, /* 290941.300628 */ |
| 573196, /* 573196.476418 */ |
| 847269, /* 847269.179851 */ |
| 1113620, /* 1113620.489452 */ |
| 1372674, /* 1372673.576986 */ |
| 1624818, /* 1624817.752104 */ |
| 1870412, /* 1870411.981536 */ |
| 2109788, /* 2109787.962654 */ |
| 2343253, /* 2343252.817465 */ |
| 2571091, /* 2571091.461923 */ |
| 2793569, /* 2793568.696416 */ |
| 3010931, /* 3010931.055901 */ |
| 3223408, /* 3223408.452106 */ |
| 3431216, /* 3431215.635215 */ |
| 3634553, /* 3634553.498355 */ |
| 3833610, /* 3833610.244726 */ |
| 4028562, /* 4028562.434393 */ |
| 4219576, /* 4219575.925308 */ |
| 4406807, /* 4406806.721144 */ |
| 4590402, /* 4590401.736809 */ |
| 4770499, /* 4770499.491025 */ |
| 4947231, /* 4947230.734179 */ |
| 5120719, /* 5120719.018555 */ |
| 5291081, /* 5291081.217197 */ |
| 5458428, /* 5458427.996830 */ |
| 5622864, /* 5622864.249668 */ |
| 5784489, /* 5784489.488298 */ |
| 5943398, /* 5943398.207380 */ |
| 6099680, /* 6099680.215452 */ |
| 6253421, /* 6253420.939751 */ |
| 6404702, /* 6404701.706649 */ |
| 6553600, /* 6553600.000000 */ |
| }; |
| |
| u8 i = 0; |
| u32 y = 0; |
| u32 d = 0; |
| u32 k = 0; |
| u32 r = 0; |
| |
| if (x == 0) |
| return 0; |
| |
| /* Scale x (normalize) */ |
| /* computing y in log(x/y) = log(x) - log(y) */ |
| if ((x & (((u32) (-1)) << (scale + 1))) == 0) { |
| for (k = scale; k > 0; k--) { |
| if (x & (((u32) 1) << scale)) |
| break; |
| x <<= 1; |
| } |
| } else { |
| for (k = scale; k < 31; k++) { |
| if ((x & (((u32) (-1)) << (scale + 1))) == 0) |
| break; |
| x >>= 1; |
| } |
| } |
| /* |
| Now x has binary point between bit[scale] and bit[scale-1] |
| and 1.0 <= x < 2.0 */ |
| |
| /* correction for divison: log(x) = log(x/y)+log(y) */ |
| y = k * ((((u32) 1) << scale) * 200); |
| |
| /* remove integer part */ |
| x &= ((((u32) 1) << scale) - 1); |
| /* get index */ |
| i = (u8) (x >> (scale - index_width)); |
| /* compute delta (x-a) */ |
| d = x & ((((u32) 1) << (scale - index_width)) - 1); |
| /* compute log, multiplication ( d* (.. )) must be within range ! */ |
| y += log2lut[i] + |
| ((d * (log2lut[i + 1] - log2lut[i])) >> (scale - index_width)); |
| /* Conver to log10() */ |
| y /= 108853; /* (log2(10) << scale) */ |
| r = (y >> 1); |
| /* rounding */ |
| if (y & ((u32)1)) |
| r++; |
| |
| return r; |
| |
| } |
| |
| /** |
| * \fn u32 frac_times1e6( u16 N, u32 D) |
| * \brief Compute: (N/D) * 1000000. |
| * \param N nominator 16-bits. |
| * \param D denominator 32-bits. |
| * \return u32 |
| * \retval ((N/D) * 1000000), 32 bits |
| * |
| * No check on D=0! |
| */ |
| static u32 frac_times1e6(u32 N, u32 D) |
| { |
| u32 remainder = 0; |
| u32 frac = 0; |
| |
| /* |
| frac = (N * 1000000) / D |
| To let it fit in a 32 bits computation: |
| frac = (N * (1000000 >> 4)) / (D >> 4) |
| This would result in a problem in case D < 16 (div by 0). |
| So we do it more elaborate as shown below. |
| */ |
| frac = (((u32) N) * (1000000 >> 4)) / D; |
| frac <<= 4; |
| remainder = (((u32) N) * (1000000 >> 4)) % D; |
| remainder <<= 4; |
| frac += remainder / D; |
| remainder = remainder % D; |
| if ((remainder * 2) > D) |
| frac++; |
| |
| return frac; |
| } |
| |
| /*============================================================================*/ |
| |
| |
| /** |
| * \brief Values for NICAM prescaler gain. Computed from dB to integer |
| * and rounded. For calc used formula: 16*10^(prescaleGain[dB]/20). |
| * |
| */ |
| static const u16 nicam_presc_table_val[43] = { |
| 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 4, 4, |
| 5, 5, 6, 6, 7, 8, 9, 10, 11, 13, 14, 16, |
| 18, 20, 23, 25, 28, 32, 36, 40, 45, |
| 51, 57, 64, 71, 80, 90, 101, 113, 127 |
| }; |
| |
| /*============================================================================*/ |
| /*== END HELPER FUNCTIONS ==*/ |
| /*============================================================================*/ |
| |
| /*============================================================================*/ |
| /*============================================================================*/ |
| /*== DRXJ DAP FUNCTIONS ==*/ |
| /*============================================================================*/ |
| /*============================================================================*/ |
| |
| /* |
| This layer takes care of some device specific register access protocols: |
| -conversion to short address format |
| -access to audio block |
| This layer is placed between the drx_dap_fasi and the rest of the drxj |
| specific implementation. This layer can use address map knowledge whereas |
| dap_fasi may not use memory map knowledge. |
| |
| * For audio currently only 16 bits read and write register access is |
| supported. More is not needed. RMW and 32 or 8 bit access on audio |
| registers will have undefined behaviour. Flags (RMW, CRC reset, broadcast |
| single/multi master) will be ignored. |
| |
| TODO: check ignoring single/multimaster is ok for AUD access ? |
| */ |
| |
| #define DRXJ_ISAUDWRITE(addr) (((((addr)>>16)&1) == 1) ? true : false) |
| #define DRXJ_DAP_AUDTRIF_TIMEOUT 80 /* millisec */ |
| /*============================================================================*/ |
| |
| /** |
| * \fn bool is_handled_by_aud_tr_if( u32 addr ) |
| * \brief Check if this address is handled by the audio token ring interface. |
| * \param addr |
| * \return bool |
| * \retval true Yes, handled by audio token ring interface |
| * \retval false No, not handled by audio token ring interface |
| * |
| */ |
| static |
| bool is_handled_by_aud_tr_if(u32 addr) |
| { |
| bool retval = false; |
| |
| if ((DRXDAP_FASI_ADDR2BLOCK(addr) == 4) && |
| (DRXDAP_FASI_ADDR2BANK(addr) > 1) && |
| (DRXDAP_FASI_ADDR2BANK(addr) < 6)) { |
| retval = true; |
| } |
| |
| return retval; |
| } |
| |
| /*============================================================================*/ |
| |
| int drxbsp_i2c_write_read(struct i2c_device_addr *w_dev_addr, |
| u16 w_count, |
| u8 *wData, |
| struct i2c_device_addr *r_dev_addr, |
| u16 r_count, u8 *r_data) |
| { |
| struct drx39xxj_state *state; |
| struct i2c_msg msg[2]; |
| unsigned int num_msgs; |
| |
| if (w_dev_addr == NULL) { |
| /* Read only */ |
| state = r_dev_addr->user_data; |
| msg[0].addr = r_dev_addr->i2c_addr >> 1; |
| msg[0].flags = I2C_M_RD; |
| msg[0].buf = r_data; |
| msg[0].len = r_count; |
| num_msgs = 1; |
| } else if (r_dev_addr == NULL) { |
| /* Write only */ |
| state = w_dev_addr->user_data; |
| msg[0].addr = w_dev_addr->i2c_addr >> 1; |
| msg[0].flags = 0; |
| msg[0].buf = wData; |
| msg[0].len = w_count; |
| num_msgs = 1; |
| } else { |
| /* Both write and read */ |
| state = w_dev_addr->user_data; |
| msg[0].addr = w_dev_addr->i2c_addr >> 1; |
| msg[0].flags = 0; |
| msg[0].buf = wData; |
| msg[0].len = w_count; |
| msg[1].addr = r_dev_addr->i2c_addr >> 1; |
| msg[1].flags = I2C_M_RD; |
| msg[1].buf = r_data; |
| msg[1].len = r_count; |
| num_msgs = 2; |
| } |
| |
| if (state->i2c == NULL) { |
| pr_err("i2c was zero, aborting\n"); |
| return 0; |
| } |
| if (i2c_transfer(state->i2c, msg, num_msgs) != num_msgs) { |
| pr_warn("drx3933: I2C write/read failed\n"); |
| return -EREMOTEIO; |
| } |
| |
| #ifdef DJH_DEBUG |
| if (w_dev_addr == NULL || r_dev_addr == NULL) |
| return 0; |
| |
| state = w_dev_addr->user_data; |
| |
| if (state->i2c == NULL) |
| return 0; |
| |
| msg[0].addr = w_dev_addr->i2c_addr; |
| msg[0].flags = 0; |
| msg[0].buf = wData; |
| msg[0].len = w_count; |
| msg[1].addr = r_dev_addr->i2c_addr; |
| msg[1].flags = I2C_M_RD; |
| msg[1].buf = r_data; |
| msg[1].len = r_count; |
| num_msgs = 2; |
| |
| pr_debug("drx3933 i2c operation addr=%x i2c=%p, wc=%x rc=%x\n", |
| w_dev_addr->i2c_addr, state->i2c, w_count, r_count); |
| |
| if (i2c_transfer(state->i2c, msg, 2) != 2) { |
| pr_warn("drx3933: I2C write/read failed\n"); |
| return -EREMOTEIO; |
| } |
| #endif |
| return 0; |
| } |
| |
| /*============================================================================*/ |
| |
| /****************************** |
| * |
| * int drxdap_fasi_read_block ( |
| * struct i2c_device_addr *dev_addr, -- address of I2C device |
| * u32 addr, -- address of chip register/memory |
| * u16 datasize, -- number of bytes to read |
| * u8 *data, -- data to receive |
| * u32 flags) -- special device flags |
| * |
| * Read block data from chip address. Because the chip is word oriented, |
| * the number of bytes to read must be even. |
| * |
| * Make sure that the buffer to receive the data is large enough. |
| * |
| * Although this function expects an even number of bytes, it is still byte |
| * oriented, and the data read back is NOT translated to the endianness of |
| * the target platform. |
| * |
| * Output: |
| * - 0 if reading was successful |
| * in that case: data read is in *data. |
| * - -EIO if anything went wrong |
| * |
| ******************************/ |
| |
| static int drxdap_fasi_read_block(struct i2c_device_addr *dev_addr, |
| u32 addr, |
| u16 datasize, |
| u8 *data, u32 flags) |
| { |
| u8 buf[4]; |
| u16 bufx; |
| int rc; |
| u16 overhead_size = 0; |
| |
| /* Check parameters ******************************************************* */ |
| if (dev_addr == NULL) |
| return -EINVAL; |
| |
| overhead_size = (IS_I2C_10BIT(dev_addr->i2c_addr) ? 2 : 1) + |
| (DRXDAP_FASI_LONG_FORMAT(addr) ? 4 : 2); |
| |
| if ((DRXDAP_FASI_OFFSET_TOO_LARGE(addr)) || |
| ((!(DRXDAPFASI_LONG_ADDR_ALLOWED)) && |
| DRXDAP_FASI_LONG_FORMAT(addr)) || |
| (overhead_size > (DRXDAP_MAX_WCHUNKSIZE)) || |
| ((datasize != 0) && (data == NULL)) || ((datasize & 1) == 1)) { |
| return -EINVAL; |
| } |
| |
| /* ReadModifyWrite & mode flag bits are not allowed */ |
| flags &= (~DRXDAP_FASI_RMW & ~DRXDAP_FASI_MODEFLAGS); |
| #if DRXDAP_SINGLE_MASTER |
| flags |= DRXDAP_FASI_SINGLE_MASTER; |
| #endif |
| |
| /* Read block from I2C **************************************************** */ |
| do { |
| u16 todo = (datasize < DRXDAP_MAX_RCHUNKSIZE ? |
| datasize : DRXDAP_MAX_RCHUNKSIZE); |
| |
| bufx = 0; |
| |
| addr &= ~DRXDAP_FASI_FLAGS; |
| addr |= flags; |
| |
| #if ((DRXDAPFASI_LONG_ADDR_ALLOWED == 1) && (DRXDAPFASI_SHORT_ADDR_ALLOWED == 1)) |
| /* short format address preferred but long format otherwise */ |
| if (DRXDAP_FASI_LONG_FORMAT(addr)) { |
| #endif |
| #if (DRXDAPFASI_LONG_ADDR_ALLOWED == 1) |
| buf[bufx++] = (u8) (((addr << 1) & 0xFF) | 0x01); |
| buf[bufx++] = (u8) ((addr >> 16) & 0xFF); |
| buf[bufx++] = (u8) ((addr >> 24) & 0xFF); |
| buf[bufx++] = (u8) ((addr >> 7) & 0xFF); |
| #endif |
| #if ((DRXDAPFASI_LONG_ADDR_ALLOWED == 1) && (DRXDAPFASI_SHORT_ADDR_ALLOWED == 1)) |
| } else { |
| #endif |
| #if (DRXDAPFASI_SHORT_ADDR_ALLOWED == 1) |
| buf[bufx++] = (u8) ((addr << 1) & 0xFF); |
| buf[bufx++] = |
| (u8) (((addr >> 16) & 0x0F) | |
| ((addr >> 18) & 0xF0)); |
| #endif |
| #if ((DRXDAPFASI_LONG_ADDR_ALLOWED == 1) && (DRXDAPFASI_SHORT_ADDR_ALLOWED == 1)) |
| } |
| #endif |
| |
| #if DRXDAP_SINGLE_MASTER |
| /* |
| * In single master mode, split the read and write actions. |
| * No special action is needed for write chunks here. |
| */ |
| rc = drxbsp_i2c_write_read(dev_addr, bufx, buf, |
| NULL, 0, NULL); |
| if (rc == 0) |
| rc = drxbsp_i2c_write_read(NULL, 0, NULL, dev_addr, todo, data); |
| #else |
| /* In multi master mode, do everything in one RW action */ |
| rc = drxbsp_i2c_write_read(dev_addr, bufx, buf, dev_addr, todo, |
| data); |
| #endif |
| data += todo; |
| addr += (todo >> 1); |
| datasize -= todo; |
| } while (datasize && rc == 0); |
| |
| return rc; |
| } |
| |
| |
| /****************************** |
| * |
| * int drxdap_fasi_read_reg16 ( |
| * struct i2c_device_addr *dev_addr, -- address of I2C device |
| * u32 addr, -- address of chip register/memory |
| * u16 *data, -- data to receive |
| * u32 flags) -- special device flags |
| * |
| * Read one 16-bit register or memory location. The data received back is |
| * converted back to the target platform's endianness. |
| * |
| * Output: |
| * - 0 if reading was successful |
| * in that case: read data is at *data |
| * - -EIO if anything went wrong |
| * |
| ******************************/ |
| |
| static int drxdap_fasi_read_reg16(struct i2c_device_addr *dev_addr, |
| u32 addr, |
| u16 *data, u32 flags) |
| { |
| u8 buf[sizeof(*data)]; |
| int rc; |
| |
| if (!data) |
| return -EINVAL; |
| |
| rc = drxdap_fasi_read_block(dev_addr, addr, sizeof(*data), buf, flags); |
| *data = buf[0] + (((u16) buf[1]) << 8); |
| return rc; |
| } |
| |
| /****************************** |
| * |
| * int drxdap_fasi_read_reg32 ( |
| * struct i2c_device_addr *dev_addr, -- address of I2C device |
| * u32 addr, -- address of chip register/memory |
| * u32 *data, -- data to receive |
| * u32 flags) -- special device flags |
| * |
| * Read one 32-bit register or memory location. The data received back is |
| * converted back to the target platform's endianness. |
| * |
| * Output: |
| * - 0 if reading was successful |
| * in that case: read data is at *data |
| * - -EIO if anything went wrong |
| * |
| ******************************/ |
| |
| static int drxdap_fasi_read_reg32(struct i2c_device_addr *dev_addr, |
| u32 addr, |
| u32 *data, u32 flags) |
| { |
| u8 buf[sizeof(*data)]; |
| int rc; |
| |
| if (!data) |
| return -EINVAL; |
| |
| rc = drxdap_fasi_read_block(dev_addr, addr, sizeof(*data), buf, flags); |
| *data = (((u32) buf[0]) << 0) + |
| (((u32) buf[1]) << 8) + |
| (((u32) buf[2]) << 16) + (((u32) buf[3]) << 24); |
| return rc; |
| } |
| |
| /****************************** |
| * |
| * int drxdap_fasi_write_block ( |
| * struct i2c_device_addr *dev_addr, -- address of I2C device |
| * u32 addr, -- address of chip register/memory |
| * u16 datasize, -- number of bytes to read |
| * u8 *data, -- data to receive |
| * u32 flags) -- special device flags |
| * |
| * Write block data to chip address. Because the chip is word oriented, |
| * the number of bytes to write must be even. |
| * |
| * Although this function expects an even number of bytes, it is still byte |
| * oriented, and the data being written is NOT translated from the endianness of |
| * the target platform. |
| * |
| * Output: |
| * - 0 if writing was successful |
| * - -EIO if anything went wrong |
| * |
| ******************************/ |
| |
| static int drxdap_fasi_write_block(struct i2c_device_addr *dev_addr, |
| u32 addr, |
| u16 datasize, |
| u8 *data, u32 flags) |
| { |
| u8 buf[DRXDAP_MAX_WCHUNKSIZE]; |
| int st = -EIO; |
| int first_err = 0; |
| u16 overhead_size = 0; |
| u16 block_size = 0; |
| |
| /* Check parameters ******************************************************* */ |
| if (dev_addr == NULL) |
| return -EINVAL; |
| |
| overhead_size = (IS_I2C_10BIT(dev_addr->i2c_addr) ? 2 : 1) + |
| (DRXDAP_FASI_LONG_FORMAT(addr) ? 4 : 2); |
| |
| if ((DRXDAP_FASI_OFFSET_TOO_LARGE(addr)) || |
| ((!(DRXDAPFASI_LONG_ADDR_ALLOWED)) && |
| DRXDAP_FASI_LONG_FORMAT(addr)) || |
| (overhead_size > (DRXDAP_MAX_WCHUNKSIZE)) || |
| ((datasize != 0) && (data == NULL)) || ((datasize & 1) == 1)) |
| return -EINVAL; |
| |
| flags &= DRXDAP_FASI_FLAGS; |
| flags &= ~DRXDAP_FASI_MODEFLAGS; |
| #if DRXDAP_SINGLE_MASTER |
| flags |= DRXDAP_FASI_SINGLE_MASTER; |
| #endif |
| |
| /* Write block to I2C ***************************************************** */ |
| block_size = ((DRXDAP_MAX_WCHUNKSIZE) - overhead_size) & ~1; |
| do { |
| u16 todo = 0; |
| u16 bufx = 0; |
| |
| /* Buffer device address */ |
| addr &= ~DRXDAP_FASI_FLAGS; |
| addr |= flags; |
| #if (((DRXDAPFASI_LONG_ADDR_ALLOWED) == 1) && ((DRXDAPFASI_SHORT_ADDR_ALLOWED) == 1)) |
| /* short format address preferred but long format otherwise */ |
| if (DRXDAP_FASI_LONG_FORMAT(addr)) { |
| #endif |
| #if ((DRXDAPFASI_LONG_ADDR_ALLOWED) == 1) |
| buf[bufx++] = (u8) (((addr << 1) & 0xFF) | 0x01); |
| buf[bufx++] = (u8) ((addr >> 16) & 0xFF); |
| buf[bufx++] = (u8) ((addr >> 24) & 0xFF); |
| buf[bufx++] = (u8) ((addr >> 7) & 0xFF); |
| #endif |
| #if (((DRXDAPFASI_LONG_ADDR_ALLOWED) == 1) && ((DRXDAPFASI_SHORT_ADDR_ALLOWED) == 1)) |
| } else { |
| #endif |
| #if ((DRXDAPFASI_SHORT_ADDR_ALLOWED) == 1) |
| buf[bufx++] = (u8) ((addr << 1) & 0xFF); |
| buf[bufx++] = |
| (u8) (((addr >> 16) & 0x0F) | |
| ((addr >> 18) & 0xF0)); |
| #endif |
| #if (((DRXDAPFASI_LONG_ADDR_ALLOWED) == 1) && ((DRXDAPFASI_SHORT_ADDR_ALLOWED) == 1)) |
| } |
| #endif |
| |
| /* |
| In single master mode block_size can be 0. In such a case this I2C |
| sequense will be visible: (1) write address {i2c addr, |
| 4 bytes chip address} (2) write data {i2c addr, 4 bytes data } |
| (3) write address (4) write data etc... |
| Addres must be rewriten because HI is reset after data transport and |
| expects an address. |
| */ |
| todo = (block_size < datasize ? block_size : datasize); |
| if (todo == 0) { |
| u16 overhead_size_i2c_addr = 0; |
| u16 data_block_size = 0; |
| |
| overhead_size_i2c_addr = |
| (IS_I2C_10BIT(dev_addr->i2c_addr) ? 2 : 1); |
| data_block_size = |
| (DRXDAP_MAX_WCHUNKSIZE - overhead_size_i2c_addr) & ~1; |
| |
| /* write device address */ |
| st = drxbsp_i2c_write_read(dev_addr, |
| (u16) (bufx), |
| buf, |
| (struct i2c_device_addr *)(NULL), |
| 0, (u8 *)(NULL)); |
| |
| if ((st != 0) && (first_err == 0)) { |
| /* at the end, return the first error encountered */ |
| first_err = st; |
| } |
| bufx = 0; |
| todo = |
| (data_block_size < |
| datasize ? data_block_size : datasize); |
| } |
| memcpy(&buf[bufx], data, todo); |
| /* write (address if can do and) data */ |
| st = drxbsp_i2c_write_read(dev_addr, |
| (u16) (bufx + todo), |
| buf, |
| (struct i2c_device_addr *)(NULL), |
| 0, (u8 *)(NULL)); |
| |
| if ((st != 0) && (first_err == 0)) { |
| /* at the end, return the first error encountered */ |
| first_err = st; |
| } |
| datasize -= todo; |
| data += todo; |
| addr += (todo >> 1); |
| } while (datasize); |
| |
| return first_err; |
| } |
| |
| /****************************** |
| * |
| * int drxdap_fasi_write_reg16 ( |
| * struct i2c_device_addr *dev_addr, -- address of I2C device |
| * u32 addr, -- address of chip register/memory |
| * u16 data, -- data to send |
| * u32 flags) -- special device flags |
| * |
| * Write one 16-bit register or memory location. The data being written is |
| * converted from the target platform's endianness to little endian. |
| * |
| * Output: |
| * - 0 if writing was successful |
| * - -EIO if anything went wrong |
| * |
| ******************************/ |
| |
| static int drxdap_fasi_write_reg16(struct i2c_device_addr *dev_addr, |
| u32 addr, |
| u16 data, u32 flags) |
| { |
| u8 buf[sizeof(data)]; |
| |
| buf[0] = (u8) ((data >> 0) & 0xFF); |
| buf[1] = (u8) ((data >> 8) & 0xFF); |
| |
| return drxdap_fasi_write_block(dev_addr, addr, sizeof(data), buf, flags); |
| } |
| |
| /****************************** |
| * |
| * int drxdap_fasi_read_modify_write_reg16 ( |
| * struct i2c_device_addr *dev_addr, -- address of I2C device |
| * u32 waddr, -- address of chip register/memory |
| * u32 raddr, -- chip address to read back from |
| * u16 wdata, -- data to send |
| * u16 *rdata) -- data to receive back |
| * |
| * Write 16-bit data, then read back the original contents of that location. |
| * Requires long addressing format to be allowed. |
| * |
| * Before sending data, the data is converted to little endian. The |
| * data received back is converted back to the target platform's endianness. |
| * |
| * WARNING: This function is only guaranteed to work if there is one |
| * master on the I2C bus. |
| * |
| * Output: |
| * - 0 if reading was successful |
| * in that case: read back data is at *rdata |
| * - -EIO if anything went wrong |
| * |
| ******************************/ |
| |
| static int drxdap_fasi_read_modify_write_reg16(struct i2c_device_addr *dev_addr, |
| u32 waddr, |
| u32 raddr, |
| u16 wdata, u16 *rdata) |
| { |
| int rc = -EIO; |
| |
| #if (DRXDAPFASI_LONG_ADDR_ALLOWED == 1) |
| if (rdata == NULL) |
| return -EINVAL; |
| |
| rc = drxdap_fasi_write_reg16(dev_addr, waddr, wdata, DRXDAP_FASI_RMW); |
| if (rc == 0) |
| rc = drxdap_fasi_read_reg16(dev_addr, raddr, rdata, 0); |
| #endif |
| |
| return rc; |
| } |
| |
| /****************************** |
| * |
| * int drxdap_fasi_write_reg32 ( |
| * struct i2c_device_addr *dev_addr, -- address of I2C device |
| * u32 addr, -- address of chip register/memory |
| * u32 data, -- data to send |
| * u32 flags) -- special device flags |
| * |
| * Write one 32-bit register or memory location. The data being written is |
| * converted from the target platform's endianness to little endian. |
| * |
| * Output: |
| * - 0 if writing was successful |
| * - -EIO if anything went wrong |
| * |
| ******************************/ |
| |
| static int drxdap_fasi_write_reg32(struct i2c_device_addr *dev_addr, |
| u32 addr, |
| u32 data, u32 flags) |
| { |
| u8 buf[sizeof(data)]; |
| |
| buf[0] = (u8) ((data >> 0) & 0xFF); |
| buf[1] = (u8) ((data >> 8) & 0xFF); |
| buf[2] = (u8) ((data >> 16) & 0xFF); |
| buf[3] = (u8) ((data >> 24) & 0xFF); |
| |
| return drxdap_fasi_write_block(dev_addr, addr, sizeof(data), buf, flags); |
| } |
| |
| /*============================================================================*/ |
| |
| /** |
| * \fn int drxj_dap_rm_write_reg16short |
| * \brief Read modify write 16 bits audio register using short format only. |
| * \param dev_addr |
| * \param waddr Address to write to |
| * \param raddr Address to read from (usually SIO_HI_RA_RAM_S0_RMWBUF__A) |
| * \param wdata Data to write |
| * \param rdata Buffer for data to read |
| * \return int |
| * \retval 0 Succes |
| * \retval -EIO Timeout, I2C error, illegal bank |
| * |
| * 16 bits register read modify write access using short addressing format only. |
| * Requires knowledge of the registermap, thus device dependent. |
| * Using DAP FASI directly to avoid endless recursion of RMWs to audio registers. |
| * |
| */ |
| |
| /* TODO correct define should be #if ( DRXDAPFASI_SHORT_ADDR_ALLOWED==1 ) |
| See comments drxj_dap_read_modify_write_reg16 */ |
| #if (DRXDAPFASI_LONG_ADDR_ALLOWED == 0) |
| static int drxj_dap_rm_write_reg16short(struct i2c_device_addr *dev_addr, |
| u32 waddr, |
| u32 raddr, |
| u16 wdata, u16 *rdata) |
| { |
| int rc; |
| |
| if (rdata == NULL) |
| return -EINVAL; |
| |
| /* Set RMW flag */ |
| rc = drxdap_fasi_write_reg16(dev_addr, |
| SIO_HI_RA_RAM_S0_FLG_ACC__A, |
| SIO_HI_RA_RAM_S0_FLG_ACC_S0_RWM__M, |
| 0x0000); |
| if (rc == 0) { |
| /* Write new data: triggers RMW */ |
| rc = drxdap_fasi_write_reg16(dev_addr, waddr, wdata, |
| 0x0000); |
| } |
| if (rc == 0) { |
| /* Read old data */ |
| rc = drxdap_fasi_read_reg16(dev_addr, raddr, rdata, |
| 0x0000); |
| } |
| if (rc == 0) { |
| /* Reset RMW flag */ |
| rc = drxdap_fasi_write_reg16(dev_addr, |
| SIO_HI_RA_RAM_S0_FLG_ACC__A, |
| 0, 0x0000); |
| } |
| |
| return rc; |
| } |
| #endif |
| |
| /*============================================================================*/ |
| |
| static int drxj_dap_read_modify_write_reg16(struct i2c_device_addr *dev_addr, |
| u32 waddr, |
| u32 raddr, |
| u16 wdata, u16 *rdata) |
| { |
| /* TODO: correct short/long addressing format decision, |
| now long format has higher prio then short because short also |
| needs virt bnks (not impl yet) for certain audio registers */ |
| #if (DRXDAPFASI_LONG_ADDR_ALLOWED == 1) |
| return drxdap_fasi_read_modify_write_reg16(dev_addr, |
| waddr, |
| raddr, wdata, rdata); |
| #else |
| return drxj_dap_rm_write_reg16short(dev_addr, waddr, raddr, wdata, rdata); |
| #endif |
| } |
| |
| |
| /*============================================================================*/ |
| |
| /** |
| * \fn int drxj_dap_read_aud_reg16 |
| * \brief Read 16 bits audio register |
| * \param dev_addr |
| * \param addr |
| * \param data |
| * \return int |
| * \retval 0 Succes |
| * \retval -EIO Timeout, I2C error, illegal bank |
| * |
| * 16 bits register read access via audio token ring interface. |
| * |
| */ |
| static int drxj_dap_read_aud_reg16(struct i2c_device_addr *dev_addr, |
| u32 addr, u16 *data) |
| { |
| u32 start_timer = 0; |
| u32 current_timer = 0; |
| u32 delta_timer = 0; |
| u16 tr_status = 0; |
| int stat = -EIO; |
| |
| /* No read possible for bank 3, return with error */ |
| if (DRXDAP_FASI_ADDR2BANK(addr) == 3) { |
| stat = -EINVAL; |
| } else { |
| const u32 write_bit = ((dr_xaddr_t) 1) << 16; |
| |
| /* Force reset write bit */ |
| addr &= (~write_bit); |
| |
| /* Set up read */ |
| start_timer = jiffies_to_msecs(jiffies); |
| do { |
| /* RMW to aud TR IF until request is granted or timeout */ |
| stat = drxj_dap_read_modify_write_reg16(dev_addr, |
| addr, |
| SIO_HI_RA_RAM_S0_RMWBUF__A, |
| 0x0000, &tr_status); |
| |
| if (stat != 0) |
| break; |
| |
| current_timer = jiffies_to_msecs(jiffies); |
| delta_timer = current_timer - start_timer; |
| if (delta_timer > DRXJ_DAP_AUDTRIF_TIMEOUT) { |
| stat = -EIO; |
| break; |
| } |
| |
| } while (((tr_status & AUD_TOP_TR_CTR_FIFO_LOCK__M) == |
| AUD_TOP_TR_CTR_FIFO_LOCK_LOCKED) || |
| ((tr_status & AUD_TOP_TR_CTR_FIFO_FULL__M) == |
| AUD_TOP_TR_CTR_FIFO_FULL_FULL)); |
| } /* if ( DRXDAP_FASI_ADDR2BANK(addr)!=3 ) */ |
| |
| /* Wait for read ready status or timeout */ |
| if (stat == 0) { |
| start_timer = jiffies_to_msecs(jiffies); |
| |
| while ((tr_status & AUD_TOP_TR_CTR_FIFO_RD_RDY__M) != |
| AUD_TOP_TR_CTR_FIFO_RD_RDY_READY) { |
| stat = drxj_dap_read_reg16(dev_addr, |
| AUD_TOP_TR_CTR__A, |
| &tr_status, 0x0000); |
| if (stat != 0) |
| break; |
| |
| current_timer = jiffies_to_msecs(jiffies); |
| delta_timer = current_timer - start_timer; |
| if (delta_timer > DRXJ_DAP_AUDTRIF_TIMEOUT) { |
| stat = -EIO; |
| break; |
| } |
| } /* while ( ... ) */ |
| } |
| |
| /* Read value */ |
| if (stat == 0) |
| stat = drxj_dap_read_modify_write_reg16(dev_addr, |
| AUD_TOP_TR_RD_REG__A, |
| SIO_HI_RA_RAM_S0_RMWBUF__A, |
| 0x0000, data); |
| return stat; |
| } |
| |
| /*============================================================================*/ |
| |
| static int drxj_dap_read_reg16(struct i2c_device_addr *dev_addr, |
| u32 addr, |
| u16 *data, u32 flags) |
| { |
| int stat = -EIO; |
| |
| /* Check param */ |
| if ((dev_addr == NULL) || (data == NULL)) |
| return -EINVAL; |
| |
| if (is_handled_by_aud_tr_if(addr)) |
| stat = drxj_dap_read_aud_reg16(dev_addr, addr, data); |
| else |
| stat = drxdap_fasi_read_reg16(dev_addr, addr, data, flags); |
| |
| return stat; |
| } |
| /*============================================================================*/ |
| |
| /** |
| * \fn int drxj_dap_write_aud_reg16 |
| * \brief Write 16 bits audio register |
| * \param dev_addr |
| * \param addr |
| * \param data |
| * \return int |
| * \retval 0 Succes |
| * \retval -EIO Timeout, I2C error, illegal bank |
| * |
| * 16 bits register write access via audio token ring interface. |
| * |
| */ |
| static int drxj_dap_write_aud_reg16(struct i2c_device_addr *dev_addr, |
| u32 addr, u16 data) |
| { |
| int stat = -EIO; |
| |
| /* No write possible for bank 2, return with error */ |
| if (DRXDAP_FASI_ADDR2BANK(addr) == 2) { |
| stat = -EINVAL; |
| } else { |
| u32 start_timer = 0; |
| u32 current_timer = 0; |
| u32 delta_timer = 0; |
| u16 tr_status = 0; |
| const u32 write_bit = ((dr_xaddr_t) 1) << 16; |
| |
| /* Force write bit */ |
| addr |= write_bit; |
| start_timer = jiffies_to_msecs(jiffies); |
| do { |
| /* RMW to aud TR IF until request is granted or timeout */ |
| stat = drxj_dap_read_modify_write_reg16(dev_addr, |
| addr, |
| SIO_HI_RA_RAM_S0_RMWBUF__A, |
| data, &tr_status); |
| if (stat != 0) |
| break; |
| |
| current_timer = jiffies_to_msecs(jiffies); |
| delta_timer = current_timer - start_timer; |
| if (delta_timer > DRXJ_DAP_AUDTRIF_TIMEOUT) { |
| stat = -EIO; |
| break; |
| } |
| |
| } while (((tr_status & AUD_TOP_TR_CTR_FIFO_LOCK__M) == |
| AUD_TOP_TR_CTR_FIFO_LOCK_LOCKED) || |
| ((tr_status & AUD_TOP_TR_CTR_FIFO_FULL__M) == |
| AUD_TOP_TR_CTR_FIFO_FULL_FULL)); |
| |
| } /* if ( DRXDAP_FASI_ADDR2BANK(addr)!=2 ) */ |
| |
| return stat; |
| } |
| |
| /*============================================================================*/ |
| |
| static int drxj_dap_write_reg16(struct i2c_device_addr *dev_addr, |
| u32 addr, |
| u16 data, u32 flags) |
| { |
| int stat = -EIO; |
| |
| /* Check param */ |
| if (dev_addr == NULL) |
| return -EINVAL; |
| |
| if (is_handled_by_aud_tr_if(addr)) |
| stat = drxj_dap_write_aud_reg16(dev_addr, addr, data); |
| else |
| stat = drxdap_fasi_write_reg16(dev_addr, |
| addr, data, flags); |
| |
| return stat; |
| } |
| |
| /*============================================================================*/ |
| |
| /* Free data ram in SIO HI */ |
| #define SIO_HI_RA_RAM_USR_BEGIN__A 0x420040 |
| #define SIO_HI_RA_RAM_USR_END__A 0x420060 |
| |
| #define DRXJ_HI_ATOMIC_BUF_START (SIO_HI_RA_RAM_USR_BEGIN__A) |
| #define DRXJ_HI_ATOMIC_BUF_END (SIO_HI_RA_RAM_USR_BEGIN__A + 7) |
| #define DRXJ_HI_ATOMIC_READ SIO_HI_RA_RAM_PAR_3_ACP_RW_READ |
| #define DRXJ_HI_ATOMIC_WRITE SIO_HI_RA_RAM_PAR_3_ACP_RW_WRITE |
| |
| /** |
| * \fn int drxj_dap_atomic_read_write_block() |
| * \brief Basic access routine for atomic read or write access |
| * \param dev_addr pointer to i2c dev address |
| * \param addr destination/source address |
| * \param datasize size of data buffer in bytes |
| * \param data pointer to data buffer |
| * \return int |
| * \retval 0 Succes |
| * \retval -EIO Timeout, I2C error, illegal bank |
| * |
| */ |
| static |
| int drxj_dap_atomic_read_write_block(struct i2c_device_addr *dev_addr, |
| u32 addr, |
| u16 datasize, |
| u8 *data, bool read_flag) |
| { |
| struct drxj_hi_cmd hi_cmd; |
| int rc; |
| u16 word; |
| u16 dummy = 0; |
| u16 i = 0; |
| |
| /* Parameter check */ |
| if (!data || !dev_addr || ((datasize % 2)) || ((datasize / 2) > 8)) |
| return -EINVAL; |
| |
| /* Set up HI parameters to read or write n bytes */ |
| hi_cmd.cmd = SIO_HI_RA_RAM_CMD_ATOMIC_COPY; |
| hi_cmd.param1 = |
| (u16) ((DRXDAP_FASI_ADDR2BLOCK(DRXJ_HI_ATOMIC_BUF_START) << 6) + |
| DRXDAP_FASI_ADDR2BANK(DRXJ_HI_ATOMIC_BUF_START)); |
| hi_cmd.param2 = |
| (u16) DRXDAP_FASI_ADDR2OFFSET(DRXJ_HI_ATOMIC_BUF_START); |
| hi_cmd.param3 = (u16) ((datasize / 2) - 1); |
| if (!read_flag) |
| hi_cmd.param3 |= DRXJ_HI_ATOMIC_WRITE; |
| else |
| hi_cmd.param3 |= DRXJ_HI_ATOMIC_READ; |
| hi_cmd.param4 = (u16) ((DRXDAP_FASI_ADDR2BLOCK(addr) << 6) + |
| DRXDAP_FASI_ADDR2BANK(addr)); |
| hi_cmd.param5 = (u16) DRXDAP_FASI_ADDR2OFFSET(addr); |
| |
| if (!read_flag) { |
| /* write data to buffer */ |
| for (i = 0; i < (datasize / 2); i++) { |
| |
| word = ((u16) data[2 * i]); |
| word += (((u16) data[(2 * i) + 1]) << 8); |
| drxj_dap_write_reg16(dev_addr, |
| (DRXJ_HI_ATOMIC_BUF_START + i), |
| word, 0); |
| } |
| } |
| |
| rc = hi_command(dev_addr, &hi_cmd, &dummy); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| |
| if (read_flag) { |
| /* read data from buffer */ |
| for (i = 0; i < (datasize / 2); i++) { |
| drxj_dap_read_reg16(dev_addr, |
| (DRXJ_HI_ATOMIC_BUF_START + i), |
| &word, 0); |
| data[2 * i] = (u8) (word & 0xFF); |
| data[(2 * i) + 1] = (u8) (word >> 8); |
| } |
| } |
| |
| return 0; |
| |
| rw_error: |
| return rc; |
| |
| } |
| |
| /*============================================================================*/ |
| |
| /** |
| * \fn int drxj_dap_atomic_read_reg32() |
| * \brief Atomic read of 32 bits words |
| */ |
| static |
| int drxj_dap_atomic_read_reg32(struct i2c_device_addr *dev_addr, |
| u32 addr, |
| u32 *data, u32 flags) |
| { |
| u8 buf[sizeof(*data)] = { 0 }; |
| int rc = -EIO; |
| u32 word = 0; |
| |
| if (!data) |
| return -EINVAL; |
| |
| rc = drxj_dap_atomic_read_write_block(dev_addr, addr, |
| sizeof(*data), buf, true); |
| |
| if (rc < 0) |
| return 0; |
| |
| word = (u32) buf[3]; |
| word <<= 8; |
| word |= (u32) buf[2]; |
| word <<= 8; |
| word |= (u32) buf[1]; |
| word <<= 8; |
| word |= (u32) buf[0]; |
| |
| *data = word; |
| |
| return rc; |
| } |
| |
| /*============================================================================*/ |
| |
| /*============================================================================*/ |
| /*== END DRXJ DAP FUNCTIONS ==*/ |
| /*============================================================================*/ |
| |
| /*============================================================================*/ |
| /*============================================================================*/ |
| /*== HOST INTERFACE FUNCTIONS ==*/ |
| /*============================================================================*/ |
| /*============================================================================*/ |
| |
| /** |
| * \fn int hi_cfg_command() |
| * \brief Configure HI with settings stored in the demod structure. |
| * \param demod Demodulator. |
| * \return int. |
| * |
| * This routine was created because to much orthogonal settings have |
| * been put into one HI API function (configure). Especially the I2C bridge |
| * enable/disable should not need re-configuration of the HI. |
| * |
| */ |
| static int hi_cfg_command(const struct drx_demod_instance *demod) |
| { |
| struct drxj_data *ext_attr = (struct drxj_data *) (NULL); |
| struct drxj_hi_cmd hi_cmd; |
| u16 result = 0; |
| int rc; |
| |
| ext_attr = (struct drxj_data *) demod->my_ext_attr; |
| |
| hi_cmd.cmd = SIO_HI_RA_RAM_CMD_CONFIG; |
| hi_cmd.param1 = SIO_HI_RA_RAM_PAR_1_PAR1_SEC_KEY; |
| hi_cmd.param2 = ext_attr->hi_cfg_timing_div; |
| hi_cmd.param3 = ext_attr->hi_cfg_bridge_delay; |
| hi_cmd.param4 = ext_attr->hi_cfg_wake_up_key; |
| hi_cmd.param5 = ext_attr->hi_cfg_ctrl; |
| hi_cmd.param6 = ext_attr->hi_cfg_transmit; |
| |
| rc = hi_command(demod->my_i2c_dev_addr, &hi_cmd, &result); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| |
| /* Reset power down flag (set one call only) */ |
| ext_attr->hi_cfg_ctrl &= (~(SIO_HI_RA_RAM_PAR_5_CFG_SLEEP_ZZZ)); |
| |
| return 0; |
| |
| rw_error: |
| return rc; |
| } |
| |
| /** |
| * \fn int hi_command() |
| * \brief Configure HI with settings stored in the demod structure. |
| * \param dev_addr I2C address. |
| * \param cmd HI command. |
| * \param result HI command result. |
| * \return int. |
| * |
| * Sends command to HI |
| * |
| */ |
| static int |
| hi_command(struct i2c_device_addr *dev_addr, const struct drxj_hi_cmd *cmd, u16 *result) |
| { |
| u16 wait_cmd = 0; |
| u16 nr_retries = 0; |
| bool powerdown_cmd = false; |
| int rc; |
| |
| /* Write parameters */ |
| switch (cmd->cmd) { |
| |
| case SIO_HI_RA_RAM_CMD_CONFIG: |
| case SIO_HI_RA_RAM_CMD_ATOMIC_COPY: |
| rc = drxj_dap_write_reg16(dev_addr, SIO_HI_RA_RAM_PAR_6__A, cmd->param6, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| rc = drxj_dap_write_reg16(dev_addr, SIO_HI_RA_RAM_PAR_5__A, cmd->param5, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| rc = drxj_dap_write_reg16(dev_addr, SIO_HI_RA_RAM_PAR_4__A, cmd->param4, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| rc = drxj_dap_write_reg16(dev_addr, SIO_HI_RA_RAM_PAR_3__A, cmd->param3, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| /* fallthrough */ |
| case SIO_HI_RA_RAM_CMD_BRDCTRL: |
| rc = drxj_dap_write_reg16(dev_addr, SIO_HI_RA_RAM_PAR_2__A, cmd->param2, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| rc = drxj_dap_write_reg16(dev_addr, SIO_HI_RA_RAM_PAR_1__A, cmd->param1, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| /* fallthrough */ |
| case SIO_HI_RA_RAM_CMD_NULL: |
| /* No parameters */ |
| break; |
| |
| default: |
| return -EINVAL; |
| break; |
| } |
| |
| /* Write command */ |
| rc = drxj_dap_write_reg16(dev_addr, SIO_HI_RA_RAM_CMD__A, cmd->cmd, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| |
| if ((cmd->cmd) == SIO_HI_RA_RAM_CMD_RESET) |
| msleep(1); |
| |
| /* Detect power down to ommit reading result */ |
| powerdown_cmd = (bool) ((cmd->cmd == SIO_HI_RA_RAM_CMD_CONFIG) && |
| (((cmd-> |
| param5) & SIO_HI_RA_RAM_PAR_5_CFG_SLEEP__M) |
| == SIO_HI_RA_RAM_PAR_5_CFG_SLEEP_ZZZ)); |
| if (!powerdown_cmd) { |
| /* Wait until command rdy */ |
| do { |
| nr_retries++; |
| if (nr_retries > DRXJ_MAX_RETRIES) { |
| pr_err("timeout\n"); |
| goto rw_error; |
| } |
| |
| rc = drxj_dap_read_reg16(dev_addr, SIO_HI_RA_RAM_CMD__A, &wait_cmd, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| } while (wait_cmd != 0); |
| |
| /* Read result */ |
| rc = drxj_dap_read_reg16(dev_addr, SIO_HI_RA_RAM_RES__A, result, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| |
| } |
| /* if ( powerdown_cmd == true ) */ |
| return 0; |
| rw_error: |
| return rc; |
| } |
| |
| /** |
| * \fn int init_hi( const struct drx_demod_instance *demod ) |
| * \brief Initialise and configurate HI. |
| * \param demod pointer to demod data. |
| * \return int Return status. |
| * \retval 0 Success. |
| * \retval -EIO Failure. |
| * |
| * Needs to know Psys (System Clock period) and Posc (Osc Clock period) |
| * Need to store configuration in driver because of the way I2C |
| * bridging is controlled. |
| * |
| */ |
| static int init_hi(const struct drx_demod_instance *demod) |
| { |
| struct drxj_data *ext_attr = (struct drxj_data *) (NULL); |
| struct drx_common_attr *common_attr = (struct drx_common_attr *) (NULL); |
| struct i2c_device_addr *dev_addr = (struct i2c_device_addr *)(NULL); |
| int rc; |
| |
| ext_attr = (struct drxj_data *) demod->my_ext_attr; |
| common_attr = (struct drx_common_attr *) demod->my_common_attr; |
| dev_addr = demod->my_i2c_dev_addr; |
| |
| /* PATCH for bug 5003, HI ucode v3.1.0 */ |
| rc = drxj_dap_write_reg16(dev_addr, 0x4301D7, 0x801, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| |
| /* Timing div, 250ns/Psys */ |
| /* Timing div, = ( delay (nano seconds) * sysclk (kHz) )/ 1000 */ |
| ext_attr->hi_cfg_timing_div = |
| (u16) ((common_attr->sys_clock_freq / 1000) * HI_I2C_DELAY) / 1000; |
| /* Clipping */ |
| if ((ext_attr->hi_cfg_timing_div) > SIO_HI_RA_RAM_PAR_2_CFG_DIV__M) |
| ext_attr->hi_cfg_timing_div = SIO_HI_RA_RAM_PAR_2_CFG_DIV__M; |
| /* Bridge delay, uses oscilator clock */ |
| /* Delay = ( delay (nano seconds) * oscclk (kHz) )/ 1000 */ |
| /* SDA brdige delay */ |
| ext_attr->hi_cfg_bridge_delay = |
| (u16) ((common_attr->osc_clock_freq / 1000) * HI_I2C_BRIDGE_DELAY) / |
| 1000; |
| /* Clipping */ |
| if ((ext_attr->hi_cfg_bridge_delay) > SIO_HI_RA_RAM_PAR_3_CFG_DBL_SDA__M) |
| ext_attr->hi_cfg_bridge_delay = SIO_HI_RA_RAM_PAR_3_CFG_DBL_SDA__M; |
| /* SCL bridge delay, same as SDA for now */ |
| ext_attr->hi_cfg_bridge_delay += ((ext_attr->hi_cfg_bridge_delay) << |
| SIO_HI_RA_RAM_PAR_3_CFG_DBL_SCL__B); |
| /* Wakeup key, setting the read flag (as suggest in the documentation) does |
| not always result into a working solution (barebones worked VI2C failed). |
| Not setting the bit works in all cases . */ |
| ext_attr->hi_cfg_wake_up_key = DRXJ_WAKE_UP_KEY; |
| /* port/bridge/power down ctrl */ |
| ext_attr->hi_cfg_ctrl = (SIO_HI_RA_RAM_PAR_5_CFG_SLV0_SLAVE); |
| /* transit mode time out delay and watch dog divider */ |
| ext_attr->hi_cfg_transmit = SIO_HI_RA_RAM_PAR_6__PRE; |
| |
| rc = hi_cfg_command(demod); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| |
| return 0; |
| |
| rw_error: |
| return rc; |
| } |
| |
| /*============================================================================*/ |
| /*== END HOST INTERFACE FUNCTIONS ==*/ |
| /*============================================================================*/ |
| |
| /*============================================================================*/ |
| /*============================================================================*/ |
| /*== AUXILIARY FUNCTIONS ==*/ |
| /*============================================================================*/ |
| /*============================================================================*/ |
| |
| /** |
| * \fn int get_device_capabilities() |
| * \brief Get and store device capabilities. |
| * \param demod Pointer to demodulator instance. |
| * \return int. |
| * \return 0 Success |
| * \retval -EIO Failure |
| * |
| * Depending on pulldowns on MDx pins the following internals are set: |
| * * common_attr->osc_clock_freq |
| * * ext_attr->has_lna |
| * * ext_attr->has_ntsc |
| * * ext_attr->has_btsc |
| * * ext_attr->has_oob |
| * |
| */ |
| static int get_device_capabilities(struct drx_demod_instance *demod) |
| { |
| struct drx_common_attr *common_attr = (struct drx_common_attr *) (NULL); |
| struct drxj_data *ext_attr = (struct drxj_data *) NULL; |
| struct i2c_device_addr *dev_addr = (struct i2c_device_addr *)(NULL); |
| u16 sio_pdr_ohw_cfg = 0; |
| u32 sio_top_jtagid_lo = 0; |
| u16 bid = 0; |
| int rc; |
| |
| common_attr = (struct drx_common_attr *) demod->my_common_attr; |
| ext_attr = (struct drxj_data *) demod->my_ext_attr; |
| dev_addr = demod->my_i2c_dev_addr; |
| |
| rc = drxj_dap_write_reg16(dev_addr, SIO_TOP_COMM_KEY__A, SIO_TOP_COMM_KEY_KEY, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| rc = drxj_dap_read_reg16(dev_addr, SIO_PDR_OHW_CFG__A, &sio_pdr_ohw_cfg, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| rc = drxj_dap_write_reg16(dev_addr, SIO_TOP_COMM_KEY__A, SIO_TOP_COMM_KEY__PRE, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| |
| switch ((sio_pdr_ohw_cfg & SIO_PDR_OHW_CFG_FREF_SEL__M)) { |
| case 0: |
| /* ignore (bypass ?) */ |
| break; |
| case 1: |
| /* 27 MHz */ |
| common_attr->osc_clock_freq = 27000; |
| break; |
| case 2: |
| /* 20.25 MHz */ |
| common_attr->osc_clock_freq = 20250; |
| break; |
| case 3: |
| /* 4 MHz */ |
| common_attr->osc_clock_freq = 4000; |
| break; |
| default: |
| return -EIO; |
| } |
| |
| /* |
| Determine device capabilities |
| Based on pinning v47 |
| */ |
| rc = drxdap_fasi_read_reg32(dev_addr, SIO_TOP_JTAGID_LO__A, &sio_top_jtagid_lo, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| ext_attr->mfx = (u8) ((sio_top_jtagid_lo >> 29) & 0xF); |
| |
| switch ((sio_top_jtagid_lo >> 12) & 0xFF) { |
| case 0x31: |
| rc = drxj_dap_write_reg16(dev_addr, SIO_TOP_COMM_KEY__A, SIO_TOP_COMM_KEY_KEY, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| rc = drxj_dap_read_reg16(dev_addr, SIO_PDR_UIO_IN_HI__A, &bid, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| bid = (bid >> 10) & 0xf; |
| rc = drxj_dap_write_reg16(dev_addr, SIO_TOP_COMM_KEY__A, SIO_TOP_COMM_KEY__PRE, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| |
| ext_attr->has_lna = true; |
| ext_attr->has_ntsc = false; |
| ext_attr->has_btsc = false; |
| ext_attr->has_oob = false; |
| ext_attr->has_smatx = true; |
| ext_attr->has_smarx = false; |
| ext_attr->has_gpio = false; |
| ext_attr->has_irqn = false; |
| break; |
| case 0x33: |
| ext_attr->has_lna = false; |
| ext_attr->has_ntsc = false; |
| ext_attr->has_btsc = false; |
| ext_attr->has_oob = false; |
| ext_attr->has_smatx = true; |
| ext_attr->has_smarx = false; |
| ext_attr->has_gpio = false; |
| ext_attr->has_irqn = false; |
| break; |
| case 0x45: |
| ext_attr->has_lna = true; |
| ext_attr->has_ntsc = true; |
| ext_attr->has_btsc = false; |
| ext_attr->has_oob = false; |
| ext_attr->has_smatx = true; |
| ext_attr->has_smarx = true; |
| ext_attr->has_gpio = true; |
| ext_attr->has_irqn = false; |
| break; |
| case 0x46: |
| ext_attr->has_lna = false; |
| ext_attr->has_ntsc = true; |
| ext_attr->has_btsc = false; |
| ext_attr->has_oob = false; |
| ext_attr->has_smatx = true; |
| ext_attr->has_smarx = true; |
| ext_attr->has_gpio = true; |
| ext_attr->has_irqn = false; |
| break; |
| case 0x41: |
| ext_attr->has_lna = true; |
| ext_attr->has_ntsc = true; |
| ext_attr->has_btsc = true; |
| ext_attr->has_oob = false; |
| ext_attr->has_smatx = true; |
| ext_attr->has_smarx = true; |
| ext_attr->has_gpio = true; |
| ext_attr->has_irqn = false; |
| break; |
| case 0x43: |
| ext_attr->has_lna = false; |
| ext_attr->has_ntsc = true; |
| ext_attr->has_btsc = true; |
| ext_attr->has_oob = false; |
| ext_attr->has_smatx = true; |
| ext_attr->has_smarx = true; |
| ext_attr->has_gpio = true; |
| ext_attr->has_irqn = false; |
| break; |
| case 0x32: |
| ext_attr->has_lna = true; |
| ext_attr->has_ntsc = false; |
| ext_attr->has_btsc = false; |
| ext_attr->has_oob = true; |
| ext_attr->has_smatx = true; |
| ext_attr->has_smarx = true; |
| ext_attr->has_gpio = true; |
| ext_attr->has_irqn = true; |
| break; |
| case 0x34: |
| ext_attr->has_lna = false; |
| ext_attr->has_ntsc = true; |
| ext_attr->has_btsc = true; |
| ext_attr->has_oob = true; |
| ext_attr->has_smatx = true; |
| ext_attr->has_smarx = true; |
| ext_attr->has_gpio = true; |
| ext_attr->has_irqn = true; |
| break; |
| case 0x42: |
| ext_attr->has_lna = true; |
| ext_attr->has_ntsc = true; |
| ext_attr->has_btsc = true; |
| ext_attr->has_oob = true; |
| ext_attr->has_smatx = true; |
| ext_attr->has_smarx = true; |
| ext_attr->has_gpio = true; |
| ext_attr->has_irqn = true; |
| break; |
| case 0x44: |
| ext_attr->has_lna = false; |
| ext_attr->has_ntsc = true; |
| ext_attr->has_btsc = true; |
| ext_attr->has_oob = true; |
| ext_attr->has_smatx = true; |
| ext_attr->has_smarx = true; |
| ext_attr->has_gpio = true; |
| ext_attr->has_irqn = true; |
| break; |
| default: |
| /* Unknown device variant */ |
| return -EIO; |
| break; |
| } |
| |
| return 0; |
| rw_error: |
| return rc; |
| } |
| |
| /** |
| * \fn int power_up_device() |
| * \brief Power up device. |
| * \param demod Pointer to demodulator instance. |
| * \return int. |
| * \return 0 Success |
| * \retval -EIO Failure, I2C or max retries reached |
| * |
| */ |
| |
| #ifndef DRXJ_MAX_RETRIES_POWERUP |
| #define DRXJ_MAX_RETRIES_POWERUP 10 |
| #endif |
| |
| static int power_up_device(struct drx_demod_instance *demod) |
| { |
| struct i2c_device_addr *dev_addr = (struct i2c_device_addr *)(NULL); |
| u8 data = 0; |
| u16 retry_count = 0; |
| struct i2c_device_addr wake_up_addr; |
| |
| dev_addr = demod->my_i2c_dev_addr; |
| wake_up_addr.i2c_addr = DRXJ_WAKE_UP_KEY; |
| wake_up_addr.i2c_dev_id = dev_addr->i2c_dev_id; |
| wake_up_addr.user_data = dev_addr->user_data; |
| /* |
| * I2C access may fail in this case: no ack |
| * dummy write must be used to wake uop device, dummy read must be used to |
| * reset HI state machine (avoiding actual writes) |
| */ |
| do { |
| data = 0; |
| drxbsp_i2c_write_read(&wake_up_addr, 1, &data, |
| (struct i2c_device_addr *)(NULL), 0, |
| (u8 *)(NULL)); |
| msleep(10); |
| retry_count++; |
| } while ((drxbsp_i2c_write_read |
| ((struct i2c_device_addr *) (NULL), 0, (u8 *)(NULL), dev_addr, 1, |
| &data) |
| != 0) && (retry_count < DRXJ_MAX_RETRIES_POWERUP)); |
| |
| /* Need some recovery time .... */ |
| msleep(10); |
| |
| if (retry_count == DRXJ_MAX_RETRIES_POWERUP) |
| return -EIO; |
| |
| return 0; |
| } |
| |
| /*----------------------------------------------------------------------------*/ |
| /* MPEG Output Configuration Functions - begin */ |
| /*----------------------------------------------------------------------------*/ |
| /** |
| * \fn int ctrl_set_cfg_mpeg_output() |
| * \brief Set MPEG output configuration of the device. |
| * \param devmod Pointer to demodulator instance. |
| * \param cfg_data Pointer to mpeg output configuaration. |
| * \return int. |
| * |
| * Configure MPEG output parameters. |
| * |
| */ |
| static int |
| ctrl_set_cfg_mpeg_output(struct drx_demod_instance *demod, struct drx_cfg_mpeg_output *cfg_data) |
| { |
| struct i2c_device_addr *dev_addr = (struct i2c_device_addr *)(NULL); |
| struct drxj_data *ext_attr = (struct drxj_data *) (NULL); |
| struct drx_common_attr *common_attr = (struct drx_common_attr *) (NULL); |
| int rc; |
| u16 fec_oc_reg_mode = 0; |
| u16 fec_oc_reg_ipr_mode = 0; |
| u16 fec_oc_reg_ipr_invert = 0; |
| u32 max_bit_rate = 0; |
| u32 rcn_rate = 0; |
| u32 nr_bits = 0; |
| u16 sio_pdr_md_cfg = 0; |
| /* data mask for the output data byte */ |
| u16 invert_data_mask = |
| FEC_OC_IPR_INVERT_MD7__M | FEC_OC_IPR_INVERT_MD6__M | |
| FEC_OC_IPR_INVERT_MD5__M | FEC_OC_IPR_INVERT_MD4__M | |
| FEC_OC_IPR_INVERT_MD3__M | FEC_OC_IPR_INVERT_MD2__M | |
| FEC_OC_IPR_INVERT_MD1__M | FEC_OC_IPR_INVERT_MD0__M; |
| |
| /* check arguments */ |
| if ((demod == NULL) || (cfg_data == NULL)) |
| return -EINVAL; |
| |
| dev_addr = demod->my_i2c_dev_addr; |
| ext_attr = (struct drxj_data *) demod->my_ext_attr; |
| common_attr = (struct drx_common_attr *) demod->my_common_attr; |
| |
| if (cfg_data->enable_mpeg_output == true) { |
| /* quick and dirty patch to set MPEG incase current std is not |
| producing MPEG */ |
| switch (ext_attr->standard) { |
| case DRX_STANDARD_8VSB: |
| case DRX_STANDARD_ITU_A: |
| case DRX_STANDARD_ITU_B: |
| case DRX_STANDARD_ITU_C: |
| break; |
| default: |
| return 0; |
| } |
| |
| rc = drxj_dap_write_reg16(dev_addr, FEC_OC_OCR_INVERT__A, 0, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| switch (ext_attr->standard) { |
| case DRX_STANDARD_8VSB: |
| rc = drxj_dap_write_reg16(dev_addr, FEC_OC_FCT_USAGE__A, 7, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } /* 2048 bytes fifo ram */ |
| rc = drxj_dap_write_reg16(dev_addr, FEC_OC_TMD_CTL_UPD_RATE__A, 10, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| rc = drxj_dap_write_reg16(dev_addr, FEC_OC_TMD_INT_UPD_RATE__A, 10, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| rc = drxj_dap_write_reg16(dev_addr, FEC_OC_AVR_PARM_A__A, 5, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| rc = drxj_dap_write_reg16(dev_addr, FEC_OC_AVR_PARM_B__A, 7, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| rc = drxj_dap_write_reg16(dev_addr, FEC_OC_RCN_GAIN__A, 10, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| /* Low Water Mark for synchronization */ |
| rc = drxj_dap_write_reg16(dev_addr, FEC_OC_SNC_LWM__A, 3, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| /* High Water Mark for synchronization */ |
| rc = drxj_dap_write_reg16(dev_addr, FEC_OC_SNC_HWM__A, 5, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| break; |
| case DRX_STANDARD_ITU_A: |
| case DRX_STANDARD_ITU_C: |
| switch (ext_attr->constellation) { |
| case DRX_CONSTELLATION_QAM256: |
| nr_bits = 8; |
| break; |
| case DRX_CONSTELLATION_QAM128: |
| nr_bits = 7; |
| break; |
| case DRX_CONSTELLATION_QAM64: |
| nr_bits = 6; |
| break; |
| case DRX_CONSTELLATION_QAM32: |
| nr_bits = 5; |
| break; |
| case DRX_CONSTELLATION_QAM16: |
| nr_bits = 4; |
| break; |
| default: |
| return -EIO; |
| } /* ext_attr->constellation */ |
| /* max_bit_rate = symbol_rate * nr_bits * coef */ |
| /* coef = 188/204 */ |
| max_bit_rate = |
| (ext_attr->curr_symbol_rate / 8) * nr_bits * 188; |
| /* pass through b/c Annex A/c need following settings */ |
| case DRX_STANDARD_ITU_B: |
| rc = drxj_dap_write_reg16(dev_addr, FEC_OC_FCT_USAGE__A, FEC_OC_FCT_USAGE__PRE, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| rc = drxj_dap_write_reg16(dev_addr, FEC_OC_TMD_CTL_UPD_RATE__A, FEC_OC_TMD_CTL_UPD_RATE__PRE, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| rc = drxj_dap_write_reg16(dev_addr, FEC_OC_TMD_INT_UPD_RATE__A, 5, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| rc = drxj_dap_write_reg16(dev_addr, FEC_OC_AVR_PARM_A__A, FEC_OC_AVR_PARM_A__PRE, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| rc = drxj_dap_write_reg16(dev_addr, FEC_OC_AVR_PARM_B__A, FEC_OC_AVR_PARM_B__PRE, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| if (cfg_data->static_clk == true) { |
| rc = drxj_dap_write_reg16(dev_addr, FEC_OC_RCN_GAIN__A, 0xD, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| } else { |
| rc = drxj_dap_write_reg16(dev_addr, FEC_OC_RCN_GAIN__A, FEC_OC_RCN_GAIN__PRE, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| } |
| rc = drxj_dap_write_reg16(dev_addr, FEC_OC_SNC_LWM__A, 2, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| rc = drxj_dap_write_reg16(dev_addr, FEC_OC_SNC_HWM__A, 12, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| break; |
| default: |
| break; |
| } /* swtich (standard) */ |
| |
| /* Check insertion of the Reed-Solomon parity bytes */ |
| rc = drxj_dap_read_reg16(dev_addr, FEC_OC_MODE__A, &fec_oc_reg_mode, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| rc = drxj_dap_read_reg16(dev_addr, FEC_OC_IPR_MODE__A, &fec_oc_reg_ipr_mode, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| if (cfg_data->insert_rs_byte == true) { |
| /* enable parity symbol forward */ |
| fec_oc_reg_mode |= FEC_OC_MODE_PARITY__M; |
| /* MVAL disable during parity bytes */ |
| fec_oc_reg_ipr_mode |= FEC_OC_IPR_MODE_MVAL_DIS_PAR__M; |
| switch (ext_attr->standard) { |
| case DRX_STANDARD_8VSB: |
| rcn_rate = 0x004854D3; |
| break; |
| case DRX_STANDARD_ITU_B: |
| fec_oc_reg_mode |= FEC_OC_MODE_TRANSPARENT__M; |
| switch (ext_attr->constellation) { |
| case DRX_CONSTELLATION_QAM256: |
| rcn_rate = 0x008945E7; |
| break; |
| case DRX_CONSTELLATION_QAM64: |
| rcn_rate = 0x005F64D4; |
| break; |
| default: |
| return -EIO; |
| } |
| break; |
| case DRX_STANDARD_ITU_A: |
| case DRX_STANDARD_ITU_C: |
| /* insert_rs_byte = true -> coef = 188/188 -> 1, RS bits are in MPEG output */ |
| rcn_rate = |
| (frac28 |
| (max_bit_rate, |
| (u32) (common_attr->sys_clock_freq / 8))) / |
| 188; |
| break; |
| default: |
| return -EIO; |
| } /* ext_attr->standard */ |
| } else { /* insert_rs_byte == false */ |
| |
| /* disable parity symbol forward */ |
| fec_oc_reg_mode &= (~FEC_OC_MODE_PARITY__M); |
| /* MVAL enable during parity bytes */ |
| fec_oc_reg_ipr_mode &= (~FEC_OC_IPR_MODE_MVAL_DIS_PAR__M); |
| switch (ext_attr->standard) { |
| case DRX_STANDARD_8VSB: |
| rcn_rate = 0x0041605C; |
| break; |
| case DRX_STANDARD_ITU_B: |
| fec_oc_reg_mode &= (~FEC_OC_MODE_TRANSPARENT__M); |
| switch (ext_attr->constellation) { |
| case DRX_CONSTELLATION_QAM256: |
| rcn_rate = 0x0082D6A0; |
| break; |
| case DRX_CONSTELLATION_QAM64: |
| rcn_rate = 0x005AEC1A; |
| break; |
| default: |
| return -EIO; |
| } |
| break; |
| case DRX_STANDARD_ITU_A: |
| case DRX_STANDARD_ITU_C: |
| /* insert_rs_byte = false -> coef = 188/204, RS bits not in MPEG output */ |
| rcn_rate = |
| (frac28 |
| (max_bit_rate, |
| (u32) (common_attr->sys_clock_freq / 8))) / |
| 204; |
| break; |
| default: |
| return -EIO; |
| } /* ext_attr->standard */ |
| } |
| |
| if (cfg_data->enable_parallel == true) { /* MPEG data output is paralel -> clear ipr_mode[0] */ |
| fec_oc_reg_ipr_mode &= (~(FEC_OC_IPR_MODE_SERIAL__M)); |
| } else { /* MPEG data output is serial -> set ipr_mode[0] */ |
| fec_oc_reg_ipr_mode |= FEC_OC_IPR_MODE_SERIAL__M; |
| } |
| |
| /* Control slective inversion of output bits */ |
| if (cfg_data->invert_data == true) |
| fec_oc_reg_ipr_invert |= invert_data_mask; |
| else |
| fec_oc_reg_ipr_invert &= (~(invert_data_mask)); |
| |
| if (cfg_data->invert_err == true) |
| fec_oc_reg_ipr_invert |= FEC_OC_IPR_INVERT_MERR__M; |
| else |
| fec_oc_reg_ipr_invert &= (~(FEC_OC_IPR_INVERT_MERR__M)); |
| |
| if (cfg_data->invert_str == true) |
| fec_oc_reg_ipr_invert |= FEC_OC_IPR_INVERT_MSTRT__M; |
| else |
| fec_oc_reg_ipr_invert &= (~(FEC_OC_IPR_INVERT_MSTRT__M)); |
| |
| if (cfg_data->invert_val == true) |
| fec_oc_reg_ipr_invert |= FEC_OC_IPR_INVERT_MVAL__M; |
| else |
| fec_oc_reg_ipr_invert &= (~(FEC_OC_IPR_INVERT_MVAL__M)); |
| |
| if (cfg_data->invert_clk == true) |
| fec_oc_reg_ipr_invert |= FEC_OC_IPR_INVERT_MCLK__M; |
| else |
| fec_oc_reg_ipr_invert &= (~(FEC_OC_IPR_INVERT_MCLK__M)); |
| |
| |
| if (cfg_data->static_clk == true) { /* Static mode */ |
| u32 dto_rate = 0; |
| u32 bit_rate = 0; |
| u16 fec_oc_dto_burst_len = 0; |
| u16 fec_oc_dto_period = 0; |
| |
| fec_oc_dto_burst_len = FEC_OC_DTO_BURST_LEN__PRE; |
| |
| switch (ext_attr->standard) { |
| case DRX_STANDARD_8VSB: |
| fec_oc_dto_period = 4; |
| if (cfg_data->insert_rs_byte == true) |
| fec_oc_dto_burst_len = 208; |
| break; |
| case DRX_STANDARD_ITU_A: |
| { |
| u32 symbol_rate_th = 6400000; |
| if (cfg_data->insert_rs_byte == true) { |
| fec_oc_dto_burst_len = 204; |
| symbol_rate_th = 5900000; |
| } |
| if (ext_attr->curr_symbol_rate >= |
| symbol_rate_th) { |
| fec_oc_dto_period = 0; |
| } else { |
| fec_oc_dto_period = 1; |
| } |
| } |
| break; |
| case DRX_STANDARD_ITU_B: |
| fec_oc_dto_period = 1; |
| if (cfg_data->insert_rs_byte == true) |
| fec_oc_dto_burst_len = 128; |
| break; |
| case DRX_STANDARD_ITU_C: |
| fec_oc_dto_period = 1; |
| if (cfg_data->insert_rs_byte == true) |
| fec_oc_dto_burst_len = 204; |
| break; |
| default: |
| return -EIO; |
| } |
| bit_rate = |
| common_attr->sys_clock_freq * 1000 / (fec_oc_dto_period + |
| 2); |
| dto_rate = |
| frac28(bit_rate, common_attr->sys_clock_freq * 1000); |
| dto_rate >>= 3; |
| rc = drxj_dap_write_reg16(dev_addr, FEC_OC_DTO_RATE_HI__A, (u16)((dto_rate >> 16) & FEC_OC_DTO_RATE_HI__M), 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| rc = drxj_dap_write_reg16(dev_addr, FEC_OC_DTO_RATE_LO__A, (u16)(dto_rate & FEC_OC_DTO_RATE_LO_RATE_LO__M), 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| rc = drxj_dap_write_reg16(dev_addr, FEC_OC_DTO_MODE__A, FEC_OC_DTO_MODE_DYNAMIC__M | FEC_OC_DTO_MODE_OFFSET_ENABLE__M, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| rc = drxj_dap_write_reg16(dev_addr, FEC_OC_FCT_MODE__A, FEC_OC_FCT_MODE_RAT_ENA__M | FEC_OC_FCT_MODE_VIRT_ENA__M, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| rc = drxj_dap_write_reg16(dev_addr, FEC_OC_DTO_BURST_LEN__A, fec_oc_dto_burst_len, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| if (ext_attr->mpeg_output_clock_rate != DRXJ_MPEGOUTPUT_CLOCK_RATE_AUTO) |
| fec_oc_dto_period = ext_attr->mpeg_output_clock_rate - 1; |
| rc = drxj_dap_write_reg16(dev_addr, FEC_OC_DTO_PERIOD__A, fec_oc_dto_period, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| } else { /* Dynamic mode */ |
| |
| rc = drxj_dap_write_reg16(dev_addr, FEC_OC_DTO_MODE__A, FEC_OC_DTO_MODE_DYNAMIC__M, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| rc = drxj_dap_write_reg16(dev_addr, FEC_OC_FCT_MODE__A, 0, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| } |
| |
| rc = drxdap_fasi_write_reg32(dev_addr, FEC_OC_RCN_CTL_RATE_LO__A, rcn_rate, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| |
| /* Write appropriate registers with requested configuration */ |
| rc = drxj_dap_write_reg16(dev_addr, FEC_OC_MODE__A, fec_oc_reg_mode, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| rc = drxj_dap_write_reg16(dev_addr, FEC_OC_IPR_MODE__A, fec_oc_reg_ipr_mode, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| rc = drxj_dap_write_reg16(dev_addr, FEC_OC_IPR_INVERT__A, fec_oc_reg_ipr_invert, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| |
| /* enabling for both parallel and serial now */ |
| /* Write magic word to enable pdr reg write */ |
| rc = drxj_dap_write_reg16(dev_addr, SIO_TOP_COMM_KEY__A, 0xFABA, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| /* Set MPEG TS pads to outputmode */ |
| rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MSTRT_CFG__A, 0x0013, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MERR_CFG__A, 0x0013, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MCLK_CFG__A, MPEG_OUTPUT_CLK_DRIVE_STRENGTH << SIO_PDR_MCLK_CFG_DRIVE__B | 0x03 << SIO_PDR_MCLK_CFG_MODE__B, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MVAL_CFG__A, 0x0013, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| sio_pdr_md_cfg = |
| MPEG_SERIAL_OUTPUT_PIN_DRIVE_STRENGTH << |
| SIO_PDR_MD0_CFG_DRIVE__B | 0x03 << SIO_PDR_MD0_CFG_MODE__B; |
| rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD0_CFG__A, sio_pdr_md_cfg, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| if (cfg_data->enable_parallel == true) { /* MPEG data output is paralel -> set MD1 to MD7 to output mode */ |
| sio_pdr_md_cfg = |
| MPEG_PARALLEL_OUTPUT_PIN_DRIVE_STRENGTH << |
| SIO_PDR_MD0_CFG_DRIVE__B | 0x03 << |
| SIO_PDR_MD0_CFG_MODE__B; |
| rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD0_CFG__A, sio_pdr_md_cfg, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD1_CFG__A, sio_pdr_md_cfg, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD2_CFG__A, sio_pdr_md_cfg, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD3_CFG__A, sio_pdr_md_cfg, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD4_CFG__A, sio_pdr_md_cfg, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD5_CFG__A, sio_pdr_md_cfg, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD6_CFG__A, sio_pdr_md_cfg, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD7_CFG__A, sio_pdr_md_cfg, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| } else { /* MPEG data output is serial -> set MD1 to MD7 to tri-state */ |
| rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD1_CFG__A, 0x0000, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD2_CFG__A, 0x0000, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD3_CFG__A, 0x0000, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD4_CFG__A, 0x0000, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD5_CFG__A, 0x0000, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD6_CFG__A, 0x0000, 0); |
| if (rc != 0) { |
| pr_err("error %d\n", rc); |
| goto rw_error; |
| } |
| rc = drxj_dap_write_reg16(dev_addr, SIO_PDR_MD7_CFG__A, 0x0000, 0); |
| if (rc != 0 |